Math.Sqrt()が静的関数なのはなぜですか?


31

静的メソッドとインスタンスメソッドに関する議論Sqrt()では、静的メソッドではなく、数値型のインスタンスメソッドである必要があると常に考えています。何故ですか?それは明らかに価値に作用します。

 // looks wrong to me
 var y = Math.Sqrt(x);
 // looks better to me
 var y = x.Sqrt();

多くの言語にはインスタンスメソッドがあるので、値型にはインスタンスメソッドを含めることができますToString()

コメントからいくつかの質問に答えるには:なぜ1.Sqrt()合法ではないのですか?1.ToString()です。

一部の言語では値型のメソッドを使用できませんが、一部の言語では使用できます。Java、ECMAScript、C#、Python(__str__(self)定義済み)など、これらについて話します。同じことがceil()floor()などの他の関数にも当てはまります。


18
これをどの言語で提案していますか?う1.sqrt()有効では?

20
彼らは方法はありませんので、多くの言語(Javaなど)でダブルスは、(パフォーマンス上の理由から)primativesある
リチャード・チンクル

45
数値型は、適用可能なあらゆる数学関数で肥大化する必要がありますか?
Dスタンレー

19
FWIW 一部の言語でクラスの前に関数を追加することを意味する場合、私はそれで大丈夫ですよりもはるかに自然にSqrt(x)見えると思います。それは場合したインスタンスメソッドそして、それはだことを示すために、より適切であろう返す値をではなく、変更するインスタンスを。x.Sqrt()x.GetSqrt()
Dスタンレー

23
この質問は、現在の形式では言語にとらわれません。それが問題の根本です。
risingDarkness

回答:


20

それは完全に言語設計の選択です。また、プリミティブ型の基礎となる実装、およびそれによるパフォーマンスの考慮事項にも依存します。

.NETには、aに作用して.aを返す静的Math.Sqrtメソッドが1つdoubledoubleだけあります。それ以外に渡すものはすべて、キャストするか、に昇格させる必要がありますdouble

double sqrt2 = Math.Sqrt(2d);

一方、これらの操作を型の関数として公開する Rustがあります

let sqrt2 = 2.0f32.sqrt();
let higher = 2.0f32.max(3.0f32);

ただし、Rustには汎用の関数呼び出し構文もあるため(以前にそのことを述べた人もいます)、好きなものを選択できます。

let sqrt2 = f32::sqrt(2.0f32);
let higher = f32::max(2.0f32, 3.0f32);

1
.NETで拡張メソッドを記述できることは注目に値するので、この実装を本当に見たい場合はx.Sqrt()、それを行うことができます。public static class DoubleExtensions { public static double Sqrt( this double self) { return Math.Sqrt(self); } }
ザカリーダウ

1
また、C#6ではSqrt(x) msdn.microsoft.com/en-us/library/sf0df423.aspxになります。
デン

65

新しい言語を設計しSqrtており、インスタンスメソッドになりたいとします。そこで、doubleクラスを見て、設計を開始します。明らかに(インスタンス以外の)入力はなく、aを返しますdouble。コードを作成してテストします。完璧。

しかし、整数の平方根を取ることも有効であり、平方根を取るためだけに全員を強制的にdoubleに変換することは望ましくありません。そこでint、設計に移り始めます。それは何を返しますか?我々は可能性を返すintと、それは完璧な正方形のためにのみ動作させる、または最寄りの結果ラウンドint(今の適切な丸め方法についての議論を無視して)。しかし、誰かが非整数の結果を望んでいる場合はどうでしょうか?2つのメソッドが必要です-1 intつはaを返し、もう1つはaを返しますdouble(一部の言語では名前を変更しないと不可能です)。そのため、を返す必要があると判断しdoubleます。実装します。しかし、実装は私たちが使用したものと同一ですdouble。コピーして貼り付けますか?インスタンスをにキャストして、そのインスタンスメソッドdoubleを呼び出しますか?両方のクラスからアクセスできるライブラリメソッドにロジックを配置しないのはなぜですか。ライブラリと関数を呼び出します。 MathMath.Sqrt

Math.Sqrt静的関数はなぜですか?:

  • 実装は、基になる数値型に関係なく同じであるため
  • 特定のインスタンスに影響を与えないため(1つの値を取り、結果を返します)
  • 数値型はその機能に依存しないため、それを別のクラスに含めることは理にかなっています

私たちは他の議論にも対処していません:

  • それは名前を付ける必要がありGetSqrt、それはので、返すインスタンスを修正するよりも、新しい値をかなり?
  • どうSquareAbsTruncLog10LnPowerFactorialSinCosArcTan

6
1.sqrt()vs 1.1.sqrt()(ガッド、見苦しい)の喜びは言うまでもありませんが、共通の基本クラスがありますか?そのsqrt()方法の契約は何ですか?

5
@MichaelT素敵な例。何が1.1.Sqrt表されているのかを理解するには、4回の読み取りが必要でした。賢い。
Dスタンレー

17
この回答から、静的クラスが主な理由にどのように役立つかは明確ではありません。Mathクラスにdouble Sqrt(int)とdouble Sqrt(double)がある場合、2つのオプションがあります。intをdoubleに変換してからdoubleバージョンを呼び出すか、適切な変更を加えてメソッドをコピーして貼り付けます(ある場合) )。ただし、これらはインスタンスバージョンについて説明したオプションとまったく同じです。あなたの他の理由(特にあなたの3番目の箇条書き)私はもっと同意します。
ベンアーロンソン

14
-1この答えは馬鹿げていますが、これは静的であることと関係がありますか?あなたしているが、それらの同じ質問のいずれかの方法への回答を決めているつもりだろう(「実装は同じである[静的機能付き]」とする虚偽の、またはそれがインスタンスメソッドのためになるよりも、少なくともこれ以上の事実です。)
BlueRaja-ダニーPflughoeft

20
「基礎となる数値型に関係なく実装は同じです」はまったくナンセンスです。平方根関数の実装は、恐ろしく非効率にならないように、使用している型に基づいて大幅に異なる必要があります。
R ..

25

多くの場合、数学演算はパフォーマンスに非常に敏感です。したがって、コンパイル時に完全に解決(および最適化、またはインライン化)できる静的メソッドを使用します。一部の言語では、静的にディスパッチされるメソッドを指定するメカニズムを提供していません。さらに、多くの言語のオブジェクトモデルにはかなりのメモリオーバーヘッドがあり、これはなどの「プリミティブ」タイプには受け入れられませんdouble

いくつかの言語では、メソッド呼び出し構文を使用する関数を定義できますが、実際には静的にディスパッチされます。C#3.0以降の拡張メソッドは一例です。C ++はプリミティブ型のメソッドをサポートしていませんが、非仮想メソッド(C ++のメソッドのデフォルトなど)も別のケースです。もちろん、さまざまなメソッドでプリミティブ型を装飾する独自のラッパークラスをC ++で作成し、実行時のオーバーヘッドを発生させることはできません。ただし、値をそのラッパータイプに手動で変換する必要があります。

数値型のメソッドを定義する言語がいくつかあります。これらは通常、すべてがオブジェクトである非常に動的な言語です。ここで、パフォーマンスは概念的な優雅さに対する二次的な考慮事項ですが、これらの言語は一般的に数値計算に使用されません。ただし、これらの言語には、プリミティブの操作を「ボックス化解除」できるオプティマイザーが含まれている場合があります。


技術的な考慮事項はさておき、このようなメソッドベースの数学インターフェイスが良いインターフェイスになるかどうかを検討できます。2つの問題が発生します。

  • 数学表記は、メソッドではなく演算子と関数に基づいています。のような表現は42.sqrt、多くのユーザーにとって、に比べてはるかに異質に見えますsqrt(42)。数学に詳しいユーザーとしては、ドットメソッド呼び出し構文よりも独自の演算子を作成する機能を好みます。
  • 単一責任原則は、あるタイプの一部である操作の数を必須操作に制限することを推奨しています。乗算と比較すると、平方根が必要になることはめったにありません。お使いの言語が具体的に統計anlysisのために意図されている場合は、(そのような動作など、よりプリミティブを提供するmeanmedianvariancestdnormalize数値のリストに、または番号のガンマ関数)が便利です。汎用言語の場合、これはインターフェイスを圧迫するだけです。重要ではない操作を別のネームスペースに委任すると、大部分のユーザーがこのタイプにアクセスしやすくなります。

Pythonは、大量の計算処理に使用される、すべてがオブジェクトである言語の良い例です。NumPyスカラーには実際には何十ものメソッドがありますが、sqrtはまだそれらの1つではありません。それらのほとんどは、のようなものですtransposeし、mean実際の主力データ構造ですnumpyの配列、との統一されたインタフェースを提供するだけです。
user2357112は

7
@ user2357112:問題は、NumPy自体がCとCythonの混合物で書かれており、Pythonの接着剤があることです。それ以外の場合、それはそれほど速くなることはありません。
ケビン

1
この答えは、長年の設計の間に到達したある種の現実世界の妥協に非常に近いと思います。他のニュースでは、それは本当にネットのセンスが行うことができるように作るんLINQの拡張機能として「Hello World」の.MAXは()私たちを可能インテリセンスに非常にvisibileます。ボーナスポイント:結果は何ですか?ボーナスボーナス、ユニコードの結果はどうなりますか...?
アンディズスミス

15

特別な目的の数学関数が大量にあるという事実に動機付けられ、すべての数学タイプにユーティリティクラスに入れたすべての関数(またはランダムなサブセット)を入力するのではありません。そうでなければ、オートコンプリートのツールチップを汚染するか、常に2つの場所を見るように強制することになります。(のsinメンバーになるのに十分重要なのDoubleか、またはMathクラス内でhtanandなどの近交系と一緒exp1pですか?)

もう1つの実用的な理由は、数値メソッドを実装するためのさまざまな方法があり、パフォーマンスと精度のトレードオフが異なる可能性があることです。JavaにはがMathありますStrictMath


言語設計者がオートコンプリートツールチップを気にしないことを望みます。同様に、どうなりMath.<^space>ますか?そのオートコンプリートツールチップも汚染されます。逆に、あなたの2番目の段落はおそらくここでのより良い答えの1つだと思います。
Qix

@Qix彼らはそうします。ただし、ここで他の人は「インターフェースを肥大化させる」と呼ぶかもしれません。
アレクサンドルドゥビンスキー

6

ここで、奇妙な対称性が働いていることを正しく観察しました。

私が言うsqrt(n)n.sqrt()、本当に重要でないかは、どちらも同じことを表しており、あなたがどちらを好むかは、何よりも個人的な好みの問題です。

そのため、特定の言語設計者から2つの構文を交換可能にするという強い議論があります。Dプログラミング言語では、これをUniform Function Call Syntaxと呼ばれる機能で既に許可しています。同様の機能がC ++の標準化のために提案れていますマークアメリーはコメントで指摘し、Pythonはあまりにもこれを可能にします。

これには問題がないわけではありません。このような基本的な構文の変更の導入は、既存のコードに広範な影響を与え、もちろん、2つの構文を異なるものを記述するものとして考えるように何十年も訓練されてきた開発者の間で議論の余地がある議論のトピックでもあります。

長い目で見れば、この2つの統一が実現可能かどうかは時間だけでわかると思いますが、間違いなく興味深い検討事項です。


Pythonはすでにこれらの構文の両方をサポートしています。すべての非静的メソッドはself最初のパラメーターとして使用され、メソッドをクラスのプロパティとしてではなく、インスタンスのプロパティとして呼び出すと、インスタンスは最初の引数として暗黙的に渡されます。したがって、私は書くことができます"foo".startswith("f")str.startswith("foo", "f")、と私は書くことができますmy_list.append(x)list.append(my_list, x)
マークアメリー

@MarkAmery良い点。これは、DやC ++の提案がするほど劇的ではありませんが、一般的な考え方に適合しています。指摘してくれてありがとう!
ComicSansMS

3

Dスタンレーの答えに加えて、ポリモーフィズムについても考えなければなりません。Math.Sqrtのようなメソッドは、常に同じ値を同じ入力に返す必要があります。静的メソッドはオーバーライドできないため、メソッドを静的にすることは、この点を明確にする良い方法です。

ToString()メソッドについて言及しました。ここでは、このメソッドをオーバーライドすることができます。そのため、(サブ)クラスは、親クラスとしてStringとして別の方法で表されます。したがって、それをインスタンスメソッドにします。


2

さて、Javaにはすべての基本型のラッパーがあります。
また、基本型はクラス型ではなく、メンバー関数もありません。

したがって、次の選択肢があります。

  1. これらのすべてのヘルパー関数をのようなプロフォーマクラスに収集しますMath
  2. 対応するラッパーの静的関数にします。
  3. 対応するラッパーのメンバー関数にします。
  4. Javaのルールを変更します。

JavaはJavaであり、支持者はそのように好きだと公言しているため、オプション4を除外しましょう。

また、オブジェクトの割り当てはかなり安価ですが無料ではないため、オプション3を除外することもできます。これを何度繰り返すと加算されます。

2つ、まだ1つは殺します:オプション2も悪い考えです。これは、すべての関数をすべてのタイプに実装しなければならないことを意味します。
を見てみると、特にそれぞれより小さいサイズの場合、多くのギャップjava.lang.Mathがありますintdouble

したがって、最終的に明確な勝者はオプション1であり、それらをすべてユーティリティ関数クラスの1か所に集めます。

オプション4に戻ると、その方向の何かが実際にかなり後で発生しました。名前を非常に長い間解決するときに、必要なクラスのすべての静的メンバーを考慮するようにコンパイラーに要求できます。 import static someclass.*;

余談ですが、他の言語には、無料の機能(オプションで名前空間を使用)に対する偏見がないか、小さな型がはるかに少ないため、そのような問題はありません。


1
Math.min()すべてのラッパータイプでバリエーションを実装する喜びを考慮してください。

#4は納得できないと思う。 Math.sqrt()Javaの残りの部分と同時に作成されたため、sqrt()を配置するという決定が下されたとき、Math「そのように」好きなJavaユーザーの歴史的なinertia性はありませんでした。にはそれほど問題はありませんがsqrt()、のオーバーロード動作Math.round()はひどいです。タイプの値を持つメンバーの構文を使用することができることfloatdouble、その問題を回避しているでしょう。
supercat

2

明示的に言及されていない点の1つは(amonがそれを暗示しているとしても)、平方根は「派生」操作と考えることができるということです。

質問には言語設計のタグが付けられているため、言語にとらわれない説明を検討する場合があります。多くの言語には異なる哲学がありますが、不変式を保存するためにカプセル化を使用することはパラダイム全体で非常に一般的です。すなわち、その型が示すように振る舞わない値を持つことを避けるため。

たとえば、マシンワードを使用した整数の実装がある場合は、おそらく何らかの方法で表現をカプセル化する必要があります(たとえば、ビットシフトが符号を変更するのを防ぐため)が、同時に、これらのビットにアクセスして、添加。

一部の言語では、クラスとプライベートメソッドでこれを実装できます。

class Int {
    public Int add(Int x) {
      // Do something with the bits
    }
    private List<Boolean> getBits() {
      // ...
    }
}

モジュールシステムの一部:

signature INT = sig
  type int
  val add : int -> int -> int
end

structure Word : INT = struct
  datatype int  = (* ... *)
  fun add x y   = (* Do something with the bits *)
  fun getBits x = (* ... *)
end

レキシカルスコープを持つもの:

(defun getAdder ()
   (let ((getBits (lambda (x) ; ...
         (add     (lambda (x y) ; Do something with the bits
     'add))

等々。しかし、どれもこれらのメカニズムのは平方根を実現するために必要ありません:それは使用して実装することができる公共の数値型のインタフェースを、ひいてはそれがカプセル化された実装の詳細にアクセスする必要はありません。

したがって、平方根の位置は、言語およびライブラリ設計者の哲学/味に帰着します。数値の「内部」に配置することを選択する場合(インスタンスメソッドにするなど)、プリミティブ操作と同じレベルに配置することを選択する場合があります(これはインスタンスメソッドを意味するか、外部に存在することを意味する場合があります)しかし、数値は内部で、いくつかは、「ヘルパー」機能のコレクションにそれを置くことを選択するかもしれません)、スタンドアロン関数または静的メソッドと同じモジュール/クラス/名前空間、例えば、いくつかのサードパーティのライブラリにそれを委任することを選択するかもしれません。


メンバー構文(C#またはvb.net拡張メソッドなど)を使用したり、メンバーシーケンスのバリエーションを使用して静的メソッドを呼び出すことを言語が許可することを妨げるものは何もありません(二重ドット構文を見たいと思います)。一次引数に適した関数のみをリストできるというインテリセンスの利点を可能にしますが、実際のメンバー演算子とのあいまいさを避けます)。
supercat

-2

JavaおよびC#では、ToStringはクラス階層のルートであるオブジェクトのメソッドであるため、すべてのオブジェクトはToStringメソッドを実装します。Integertypeの場合、ToStringの実装がこのように機能するのは当然です。

だから、あなたの推論は間違っています。値型がToStringを実装する理由は、一部の人々が次のようなものではなかったということではありません。これは、ToStringがすでに存在し、出力するのが「最も自然な」ものだからです。


1
もちろん持っているすなわち意思決定の決定であるobject持っているToString()方法を。それはあなたの言葉にあります。「一部の人々は次のようでした。値型にToStringメソッドを使用してみましょう」。
レジデューム

-3

String.substringとは異なり、Number.sqrtは実際には数値の属性ではなく、数値に基づいた新しい結果です。数値を2乗関数に渡す方がより直感的だと思います。

さらに、Mathオブジェクトには他の静的メンバーが含まれており、それらをまとめて均一に使用する方が理にかなっています。


3
カウンターの例:BigInteger.pow()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.