เราสามารถทำอะไรเพื่อปรับปรุงความสามารถในการอ่านของโค้ดเชิงคณิตศาสตร์ใน C #, Java และสิ่งที่คล้ายกัน? [ปิด]


16

ในฐานะที่เป็นทั้งโปรแกรมเมอร์ C และโปรแกรมเมอร์ C # สิ่งหนึ่งที่ฉันไม่ชอบเกี่ยวกับ C # ก็คือฟังก์ชั่นคณิตศาสตร์ verbose เป็นอย่างไร ทุกครั้งที่คุณต้องใช้ฟังก์ชัน Sin, cosine หรือ power คุณจะต้องเพิ่มคลาสสถิตแบบคณิตศาสตร์ล่วงหน้า สิ่งนี้นำไปสู่โค้ดที่ยาวมากเมื่อสมการนั้นง่ายมาก ปัญหาจะยิ่งแย่ลงหากคุณต้องการพิมพ์ข้อมูลชนิด ดังนั้นในความคิดของฉันการอ่านได้รับความเดือดร้อน ตัวอย่างเช่น:

double x =  -Math.Cos(X) * Math.Sin(Z) + Math.Sin(X) * Math.Sin(Y) * Math.Cos(Z);

ตรงข้ามกับเพียงแค่

double x = -cos(X) * sin(Z) + sin(X) * sin(Y) * cos(Z);

นี่เป็นกรณีใน langauges อื่น ๆ เช่น Java

ฉันไม่แน่ใจว่าคำถามนี้มีทางออกหรือไม่ แต่ฉันต้องการทราบว่ามีคำแนะนำ C # หรือโปรแกรมเมอร์ Java ที่ใช้ในการปรับปรุงการอ่านรหัสคณิตศาสตร์หรือไม่ ฉันรู้ว่า C # / Java / etc ไม่ใช่ภาษาเชิงคณิตศาสตร์เช่น MATLAB หรือภาษาที่คล้ายกันดังนั้นจึงเหมาะสม แต่บางครั้งก็ยังต้องเขียนรหัสคณิตศาสตร์และมันจะดีมากหากสามารถทำให้อ่านง่ายขึ้น


ฉันไม่รู้อะไรเป็นพิเศษ แต่คุณอาจพบห้องสมุดพีชคณิตที่จะอนุญาตให้คุณกำหนดฟังก์ชันคณิตศาสตร์ด้วยสตริงแม้ว่าจะมีการปรับประสิทธิภาพบางอย่าง
raptortech97


7
คุณกังวลเกี่ยวกับการใช้คำฟุ่มเฟื่อยเล็กน้อย แต่ยังซ่อน '+' ในหมู่ '*' อย่างมีความสุขกับผู้ประกอบการเอก - ทั้งหมดโดยไม่ต้องวงเล็บปีกกา - ฉันสงสัยว่าคุณมีลำดับความสำคัญผิด
mattnz

1
มันเป็นเพียงตัวอย่าง แต่จุดที่ดี
9a3eedi

6
ใน C # 6.0 using System.Math; … double x = -Cos(X) * Sin(Z) + Sin(X) * Sin(Y) * Cos(Z);คุณจะสามารถที่จะเขียน:
svick

คำตอบ:


14

คุณสามารถกำหนดฟังก์ชั่นท้องถิ่นที่เรียกฟังก์ชั่นคงที่ทั่วโลก หวังว่าคอมไพเลอร์จะอินไลน์เครื่องห่อและจากนั้นคอมไพเลอร์ JIT จะผลิตรหัสการชุมนุมแน่นสำหรับการดำเนินงานจริง ตัวอย่างเช่น:

class MathHeavy
{
    private double sin(double x) { return Math.sin(x); }
    private double cos(double x) { return Math.cos(x); }

    public double foo(double x, double y)
    {
        return sin(x) * cos(y) - cos(x) * sin(y);
    }
}

คุณสามารถสร้างฟังก์ชั่นที่รวมการดำเนินการทางคณิตศาสตร์ทั่วไปไว้ในการดำเนินการเดี่ยวได้ สิ่งนี้จะลดจำนวนอินสแตนซ์ที่ฟังก์ชันชอบsinและcosปรากฏในโค้ดของคุณซึ่งจะทำให้ความ clunkiness ของการเรียกใช้ฟังก์ชันสแตติกแบบโกลบอลน้อยลงอย่างเห็นได้ชัด ตัวอย่างเช่น:

public Point2D rotate2D(double angle, Point2D p)
{
    double x = p.x * Math.cos(angle) - p.y * Math.sin(angle);
    double y = p.x * Math.sin(angle) + p.y * Math.cos(angle);

    return new Point2D(x, y)
}

คุณกำลังทำงานในระดับของคะแนนและการหมุนและฟังก์ชั่นตรีโกณมิติพื้นฐานจะถูกฝังอยู่


... ทำไมฉันไม่คิดอย่างนั้น :)
9a3eedi

ฉันทำเครื่องหมายว่านี่เป็นคำตอบที่ถูกต้องเพราะมันเป็นโซลูชันข้ามแพลตฟอร์มที่ง่ายพอ โซลูชันอื่น ๆ นั้นถูกต้องเช่นกัน ฉันไม่อยากจะเชื่อเลยว่าฉันจะไม่คิดอย่างนั้น :) มันชัดเจนเกินไป
9a3eedi

31

ภายใน Java มีเครื่องมือมากมายที่จะทำให้บางสิ่งน้อยลงอย่างมากคุณเพียงแค่ต้องรับรู้ สิ่งที่มีประโยชน์ในกรณีนี้คือstaticการนำเข้า ( หน้าบทช่วยสอน , วิกิพีเดีย )

ในกรณีนี้,

import static java.lang.Math.*;

class Demo {
    public static void main (String[] args) {
        double X = 42.0;
        double Y = 4.0;
        double Z = PI;

        double x =  -cos(X) * sin(Z) + sin(X) * sin(Y) * cos(Z);
        System.out.println(x);
    }
}

ทำงานค่อนข้างดี ( ideone ) มันเป็นหนักบิตส่งที่จะทำการนำเข้าคงที่ของทั้งหมดของชนชั้นคณิตศาสตร์ แต่ถ้าคุณกำลังทำมากของคณิตศาสตร์แล้วก็อาจจะมีการเรียกร้องให้

การอิมพอร์ตแบบสแตติกช่วยให้คุณสามารถอิมพอร์ตฟิลด์หรือเมธอดแบบสแตติกลงในเนมสเปซของคลาสนี้และเรียกใช้โดยไม่ต้องใช้ชื่อแพ็กเกจ คุณมักจะพบสิ่งนี้ในกรณีทดสอบ Junit ที่import static org.junit.Assert.*;พบว่าได้รับการยืนยันทั้งหมดที่มีอยู่


คำตอบที่ยอดเยี่ยม ฉันไม่ทราบคุณสมบัตินี้ Java รุ่นใดที่เป็นไปได้ภายใต้
9a3eedi

@ 9a3eedi เป็นครั้งแรกที่มีให้ใน Java 1.5

เทคนิคที่ดี ฉันชอบมัน. +1
Randall Cook

1
@ RandallCook ใน Java 1.4 วันผู้คนจะทำสิ่งต่าง ๆ เช่นpublic interface Constants { final static public double PI = 3.14; }นั้นpublic class Foo implements Constantsในทุกชั้นเรียนเพื่อเข้าถึงค่าคงที่ในส่วนต่อประสาน นี้ทำใหญ่ระเบียบ ดังนั้นด้วย 1.5 การนำเข้าแบบคงที่จึงถูกเพิ่มเข้ามาเพื่อให้สามารถดึงค่าคงที่และฟังก์ชันแบบคงที่ได้โดยไม่ต้องใช้ส่วนต่อประสาน

3
คุณสามารถเลือกฟังก์ชั่นบางอย่างได้import static java.lang.Math.cos;
วงล้อประหลาด

5

ด้วย C # 6.0 คุณสามารถใช้คุณสมบัติของการนำเข้าแบบคงที่

รหัสของคุณอาจเป็น:

using static System.Math;
using static System.Console;
namespace SomeTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            double X = 123;
            double Y = 5;
            double Z = 10;
            double x = -Cos(X) * Sin(Z) + Sin(X) * Sin(Y) * Cos(Z);
            WriteLine(x); //Without System, since it is imported 
        }
    }
}

โปรดดู: การใช้คำสั่งคงที่ (การแสดงตัวอย่างภาษา AC # 6.0)

อีกคุณสมบัติหนึ่งของ C # 6.0“ น้ำตาลเชิงกล” คือการแนะนำการใช้แบบคงที่ ด้วยคุณสมบัตินี้คุณสามารถกำจัดการอ้างอิงที่ชัดเจนกับประเภทเมื่อเรียกใช้วิธีการคงที่ นอกจากนี้การใช้สแตติกช่วยให้คุณแนะนำวิธีการขยายในคลาสที่เฉพาะเจาะจงมากกว่าวิธีการขยายทั้งหมดภายในเนมสเปซ

แก้ไข: ตั้งแต่ Visual Studio 2015 CTP ออกในเดือนมกราคมปี 2015 staticการนำเข้าคงต้องใช้คำหลักอย่างชัดเจน ชอบ:

using static System.Console;

4

นอกเหนือจากคำตอบที่ดีอื่น ๆ ที่นี่ฉันอาจแนะนำDSLสำหรับสถานการณ์ที่มีความซับซ้อนทางคณิตศาสตร์อย่างมาก (ไม่ใช่กรณีการใช้งานโดยเฉลี่ย แต่อาจมีบางโครงการทางการเงินหรือทางวิชาการ)

ด้วยเครื่องมือการสร้าง DSL เช่นXtextคุณสามารถกำหนดไวยากรณ์ทางคณิตศาสตร์ที่เรียบง่ายของคุณเองซึ่งสามารถสร้างคลาสที่มีการแสดง Java (หรือภาษาอื่น ๆ ) ของสูตรของคุณ

การแสดงออก DSL:

domain GameMath {
    formula CalcLinearDistance(double): sqrt((x2 - x1)^2 + (y2 - y1)^2)
}

สร้างผลลัพธ์:

public class GameMath {
    public static double CalcLinearDistance(int x1, int x2, int y1, int y2) {
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    }
}

ในตัวอย่างง่ายๆประโยชน์ของการสร้างไวยากรณ์และปลั๊กอิน Eclipse จะไม่คุ้มค่า แต่สำหรับโครงการที่ซับซ้อนมากขึ้นมันอาจให้ผลประโยชน์ที่ดีโดยเฉพาะอย่างยิ่งถ้า DSL เปิดใช้งานนักธุรกิจหรือนักวิจัยทางวิชาการเพื่อรักษาเอกสารทางการในที่สะดวกสบาย ภาษาและมั่นใจได้ว่างานของพวกเขาได้รับการแปลอย่างถูกต้องเป็นภาษาการดำเนินงานของโครงการ


8
ใช่โดยทั่วไปและตามคำนิยาม DSL อาจมีประโยชน์เมื่อคุณทำงานในโดเมนที่ระบุ อย่างไรก็ตามหาก DSL นี้ไม่มีอยู่หรือถ้ามันไม่พอดีกับความต้องการคุณต้องบำรุงรักษาซึ่งอาจเป็นปัญหาได้ นอกจากนี้สำหรับคำถามที่เฉพาะเจาะจง ("ฉันจะใช้บาป, cos, ... วิธีการ / ฟังก์ชั่นโดยไม่ต้องเขียนคลาสคณิตศาสตร์ทุกครั้ง"), DSL อาจเป็นทางออกที่มีขนาดใหญ่
mgoeminne

4

ใน C # คุณสามารถใช้วิธีการขยาย

ด้านล่างอ่านค่อนข้างสวยเมื่อคุณคุ้นเคยกับสัญกรณ์ "postfix":

public static class DoubleMathExtensions
{
    public static double Cos(this double n)
    {
        return Math.Cos(n);
    }

    public static double Sin(this double n)
    {
        return Math.Sin(n);
    }

    ...
}

var x =  -X.Cos() * Z.Sin() + X.Sin() * Y.Sin() * Z.Cos();

น่าเสียดายที่การมีตัวดำเนินการมาก่อนทำให้สิ่งต่าง ๆ น่าเกลียดเล็กน้อยเมื่อต้องจัดการกับจำนวนลบที่นี่ หากคุณต้องการคำนวณMath.Cos(-X)แทนคุณ-Math.Cos(X)จะต้องใส่ตัวเลขในวงเล็บ:

var x = (-X).Cos() ...

1
อนึ่งนี่จะเป็นกรณีการใช้งานที่ดีสำหรับคุณสมบัติส่วนขยายและแม้แต่กรณีการใช้งานที่ถูกต้องสำหรับการใช้งานคุณสมบัติในทางที่ผิด!
Jörg W Mittag

นี่คือสิ่งที่ฉันคิด x.Sin()จะใช้เวลาปรับ แต่ฉันใช้วิธีการขยายและนี่คือความชอบส่วนตัวครั้งแรกของฉัน
WernerCD

2

C #: รูปแบบของคำตอบของ Randall Cookซึ่งฉันชอบเพราะมันรักษาทางคณิตศาสตร์ "ลุค" ของรหัสมากกว่าวิธีการขยายคือการใช้ wrapper แต่ใช้ฟังก์ชั่นการอ้างอิงสำหรับการโทรมากกว่าห่อ ส่วนตัวแล้วฉันคิดว่ามันทำให้โค้ดดูสะอาดตา แต่โดยพื้นฐานแล้วมันก็ทำสิ่งเดียวกัน

ฉันเคาะโปรแกรมทดสอบ LINQPad ขึ้นเล็กน้อยซึ่งรวมถึงฟังก์ชั่นที่ห่อของ Randall การอ้างอิงฟังก์ชั่นของฉันและการโทรโดยตรง

ฟังก์ชั่นการอ้างอิงการโทรโดยทั่วไปใช้เวลาเดียวกันกับการโทรโดยตรง ฟังก์ชั่นห่อจะช้าลงอย่างสม่ำเสมอ - แม้ว่าจะไม่มาก

นี่คือรหัส:

void Main()
{
    MyMathyClass mmc = new MyMathyClass();

    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    for(int i = 0; i < 50000000; i++)
        mmc.DoStuff(1, 2, 3);

    "Function reference:".Dump();
    sw.Elapsed.Dump();      
    sw.Restart();

    for(int i = 0; i < 50000000; i++)
        mmc.DoStuffWrapped(1, 2, 3);

    "Wrapped function:".Dump();
    sw.Elapsed.Dump();      
    sw.Restart();

    "Direct call:".Dump();
    for(int i = 0; i < 50000000; i++)
        mmc.DoStuffControl(1, 2, 3);

    sw.Elapsed.Dump();
}

public class MyMathyClass
{
    // References
    public Func<double, double> sin;
    public Func<double, double> cos;
    public Func<double, double> tan;
    // ...

    public MyMathyClass()
    {
        sin = System.Math.Sin;
        cos = System.Math.Cos;
        tan = System.Math.Tan;
        // ...
    }

    // Wrapped functions
    public double wsin(double x) { return Math.Sin(x); }
    public double wcos(double x) { return Math.Cos(x); }
    public double wtan(double x) { return Math.Tan(x); }

    // Calculation functions
    public double DoStuff(double x, double y, double z)
    {
        return sin(x) + cos(y) + tan(z);
    }

    public double DoStuffWrapped(double x, double y, double z)
    {
        return wsin(x) + wcos(y) + wtan(z);
    }

    public double DoStuffControl(double x, double y, double z)
    {
        return Math.Sin(x) + Math.Cos(y) + Math.Tan(z);
    }
}

ผล:

Function reference:
00:00:06.5952113

Wrapped function:
00:00:07.2570828

Direct call:
00:00:06.6396096

1

ใช้ Scala! คุณสามารถกำหนดโอเปอเรเตอร์สัญลักษณ์และไม่จำเป็นต้องมี parens สำหรับเมธอดของคุณ ทำให้การตีความทางคณิตศาสตร์ง่ายขึ้น

ตัวอย่างเช่นการคำนวณเดียวกันใน Scala และ Java อาจเป็นสิ่งที่ต้องการ:

// Scala
def angle(u: Vec, v: Vec) = (u*v) / sqrt((u*u)*(v*v))

// Java
public double angle(u: Vec, v: Vec) {
  return u.dot(v) / sqrt(u.dot(u)*v.dot(v));
}

สิ่งนี้จะเพิ่มขึ้นอย่างรวดเร็ว


2
Scala ไม่สามารถใช้ได้กับ CLR มีเพียง JVM เท่านั้น ดังนั้นจึงไม่ใช่ทางเลือกที่เป็นไปได้สำหรับ C #
Ben rudgers

@benrudgers - C # ไม่ได้ทำงานกับ JVM ดังนั้นจึงไม่ใช่ทางเลือกที่เป็นไปได้สำหรับ Java ซึ่งคำถามก็ถามด้วยเช่นกัน คำถามไม่ได้ระบุว่าต้องเป็น CLR!
Rex Kerr

บางทีฉันอาจเป็น Luddite แต่มีอักขระพิเศษสองตัวสำหรับ "dot" แทนที่จะเป็น "*" โดยมีประโยชน์ว่ารหัสชัดเจนยิ่งขึ้นดูเหมือนว่าเป็นราคาขนาดเล็กที่ต้องจ่าย ยังเป็นคำตอบที่ดี
user949300
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.