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
많은 언어들 (예를 들어, 자바)에서 복식은 (성능상의 이유로) 원시적이므로 방법이 없다
Richard Tingle

45
그렇다면 숫자 유형은 가능한 모든 수학적 함수로 부풀려 져야합니까?
D Stanley

19
FWIW 나는 그것이 훨씬 더 자연스럽게 Sqrt(x)보인다고 생각합니다. 만약 그것이 어떤 언어에서 클래스 앞에 함수를 추가한다는 것을 의미한다면 나는 그것에 동의 합니다. 이 경우 했다 인스턴스 메서드 다음 그것의 것을 나타 내기 위해 더 적합 할 것이다 반환 값을하기보다는 수정 인스턴스를. x.Sqrt()x.GetSqrt()
D Stanley

23
이 질문은 현재 형태에서 언어에 구애받지 않습니다. 이것이 문제 의 근원 입니다.
상승 Darkness

답변:


20

전적으로 언어 디자인의 선택입니다. 또한 기본 유형의 기본 구현 및 그로 인한 성능 고려 사항에 따라 다릅니다.

.NET에는 a에서 작동하고 a 를 반환하는 정적 Math.Sqrt메서드가 하나만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); } }
Zachary Dow

1
또한 C # 6 에서는 Sqrt(x) msdn.microsoft.com/en-us/library/sf0df423.aspx 일 수 있습니다 .
Den

65

새로운 언어를 디자인하고 Sqrt인스턴스 메소드 가되고 싶다고 가정 해 봅시다 . 그래서 우리는 double수업 을보고 디자인을 시작합니다. 분명히 인스턴스가 아닌 입력이 없으며 a를 반환합니다 double. 코드를 작성하고 테스트합니다. 완전.

그러나 정수의 제곱근을 취하는 것도 유효하며, 우리는 모든 사람이 제곱근을 취하기 위해 이중으로 변환하도록 강요하지 않습니다. 그래서 우리는 int디자인으로 이동 하고 시작합니다. 무엇을 반환합니까? 우리 는를 반환하고 int완벽한 제곱에서만 작동하도록 만들거나 결과를 가장 가까운 int것으로 반올림 할 수 있습니다 (현재 올바른 반올림 방법에 대한 토론은 무시). 그러나 누군가 정수가 아닌 결과를 원한다면 어떻게해야합니까? 두 가지 메소드가 있어야합니다. int하나는 a를 리턴하고 다른 하나는 a를 리턴합니다 double(일부 언어에서는 이름을 변경하지 않고는 불가능). 따라서 우리는 a를 반환하기로 결정합니다 double. 이제 구현합니다. 그러나 구현은 우리가 사용한 것과 동일합니다.double. 우리는 복사하여 붙여 넣습니까? 인스턴스를에 캐스트하고 해당 인스턴스 메소드를 double호출 합니까? 두 클래스에서 액세스 할 수있는 라이브러리 메소드에 논리를 넣지 마십시오. 라이브러리 와 함수를 호출합니다 . MathMath.Sqrt

Math.Sqrt정적 함수입니까? :

  • 기본 숫자 유형에 관계없이 구현이 동일하므로
  • 특정 인스턴스에 영향을 미치지 않기 때문에 (한 값을 가져와 결과를 반환 함)
  • 숫자 유형은 해당 기능에 의존 하지 않으므로 별도의 클래스에 포함하는 것이 좋습니다.

우리는 다른 주장들도 다루지 않았습니다 :

  • 그것은 이름을 지정해야합니다 GetSqrt그 이후 반환 인스턴스를 수정하는 것보다 새로운 가치를 오히려?
  • 무엇에 대해 Square? Abs? Trunc? Log10? Ln? Power? Factorial? Sin? Cos? ArcTan?

6
아니 기쁨의 언급 1.sqrt()1.1.sqrt()그들은 공통 기본 클래스를해야합니까 (추한 보이는, ghads를)? 그 sqrt()방법에 대한 계약은 무엇입니까 ?

5
@MichaelT 좋은 예입니다. 무엇이 1.1.Sqrt대표 되는지 이해하기 위해 네 번의 독서가 필요했습니다 . 영리한.
D Stanley

17
이 대답에서 정적 클래스가 주요 이유에 어떻게 도움이되는지 확실하지 않습니다. Math 클래스에 double Sqrt (int) 및 double Sqrt (double)가있는 경우 두 가지 옵션이 있습니다. int를 double로 변환 한 다음 double 버전으로 호출하거나 적절한 변경 사항으로 메소드를 복사하여 붙여 넣기 (있는 경우) ). 그러나 이것들은 인스턴스 버전에 대해 설명한 것과 동일한 옵션입니다. 다른 추론 (특히 세 번째 글 머리 기호) 나는 더 동의합니다.
벤 애런

14
-1이 답변은 터무니 없으며,이 어떤 것이 정적 인 것과 관련이 있습니까? 당신은 어느 쪽의 방법 으로든 같은 질문들에 대한 답을 결정할 것입니다. (그리고 "[정적 함수로] 구현이 같다"는 거짓이거나, 적어도 예를 들어 메소드에 대한 것보다 더 이상 진실이 아닙니다.)
BlueRaja-대니 Pflughoeft 21시 52 분

20
"기본 숫자 유형에 관계없이 구현은 동일합니다"는 말도 안됩니다. 제곱근 함수의 구현은 끔찍하게 비효율적이지 않기 위해 작업하는 유형에 따라 크게 달라야합니다.
R ..

25

수학 연산은 종종 성능에 매우 민감합니다. 따라서 컴파일 타임에 완전히 해결 (및 최적화 또는 인라인) 할 수있는 정적 메소드를 사용하려고합니다. 일부 언어는 정적으로 전달 된 메소드를 지정하는 메커니즘을 제공하지 않습니다. 더욱이, 많은 언어의 객체 모델은와 같은 "기본"유형에는 수용 할 수없는 상당한 메모리 오버 헤드를 가지고 있습니다 double.

일부 언어를 사용하면 메소드 호출 구문을 사용하는 함수를 정의 할 수 있지만 실제로는 정적으로 전달됩니다. C # 3.0 이상의 확장 방법이 예입니다. C ++가 기본 유형에 대한 메소드를 지원하지 않지만 비가 상 메소드 (예 : C ++의 메소드 기본값)도 있습니다. 물론 C ++에서 런타임 오버 헤드없이 다양한 메소드로 기본 유형을 장식하는 고유 한 랩퍼 클래스를 작성할 수 있습니다. 그러나 값을 해당 랩퍼 유형으로 수동 변환해야합니다.

숫자 유형에 대한 메소드를 정의하는 몇 가지 언어가 있습니다. 이들은 일반적으로 모든 것이 객체 인 매우 동적 인 언어입니다. 여기서 성능은 개념적 우아함에 대한 두 번째 고려 사항이지만 이러한 언어는 일반적으로 숫자 크 런칭에 사용되지 않습니다. 그러나 이러한 언어에는 프리미티브에 대한 조작을 "개봉"할 수있는 최적화 프로그램이있을 수 있습니다.


기술적 인 고려 사항을 벗어나면 그러한 방법 기반 수학 인터페이스가 좋은 인터페이스가 될지 여부를 고려할 수 있습니다. 두 가지 문제가 발생합니다.

  • 수학 표기법은 메서드가 아닌 연산자와 함수를 기반으로합니다. 와 같은 표현 42.sqrt은보다 많은 사용자에게 훨씬 더 외계인 처럼 보일 것 sqrt(42)입니다. 수학을 많이 사용하는 사용자는 도트 메서드 호출 구문보다 고유 한 연산자를 만드는 기능을 선호합니다.
  • 단일 책임 원칙 (Single Responsibility Principle)은 유형의 일부 작업을 필수 작업으로 제한하도록 권장합니다. 곱셈과 비교할 때, 거의 제곱근이 거의 필요하지 않습니다. 언어가 특별히 더 후 프리미티브 제공, 통계 anlysis를 대상으로하는 경우 (예 : 작업 등을 mean, median, variance, std, normalize숫자 목록 또는 번호에 대한 감마 함수에) 유용 할 수 있습니다. 범용 언어의 경우 인터페이스의 무게가 가중됩니다. 필수적이지 않은 작업을 별도의 네임 스페이스로 위임하면 대부분의 사용자가 유형에보다 쉽게 ​​액세스 할 수 있습니다.

파이썬은 많은 수의 크 런칭에 사용되는 객체 언어의 좋은 예입니다. NumPy 스칼라에는 실제로 수십 가지 방법이 있지만 sqrt는 여전히 그중 하나가 아닙니다. 그들 대부분은 같은 것들 transposemean그 실제 주력 데이터 구조입니다 NumPy와 배열과 균일 한 인터페이스를 제공하기 위해 단지가있다.
user2357112는

7
@ user2357112 : NumPy 자체는 C와 Cython을 파이썬 접착제로 혼합하여 작성되었습니다. 그렇지 않으면 결코 빠를 수 없습니다.
케빈

1
이 답변은 수년간의 디자인에서 도달 한 실제 타협과 거의 일치한다고 생각합니다. 다른 뉴스에서, 정말 닷넷에서 의미가 할 수있을 수 있도록 않는 LINQ 확장으로 "안녕하세요".Max ()는 우리를 수 인텔리 매우 공개 대상 있습니다. 보너스 포인트 : 결과는 무엇입니까? 보너스 보너스, 유니 코드의 결과는 무엇입니까?
Andyz Smith

15

나는 많은 특수 목적의 수학 함수가 있으며 모든 수학 유형을 유틸리티 클래스에 넣는 모든 함수 (또는 임의의 하위 집합)로 채우는 대신 많은 수의 특수 수학 함수가 있다는 사실에 동기가 부여됩니다. 그렇지 않으면 자동 완성 툴팁을 오염 시키거나 사람들이 항상 두 곳을 보도록 강요합니다. (가 sin의 구성원이 될만큼 중요한 Double, 또는 그것을입니다 Math같은 동종 번식과 함께 클래스 htanexp1p?)

또 다른 실질적인 이유는 다른 성능과 정밀한 절충으로 숫자 방법을 구현하는 다른 방법이있을 수 있다는 것입니다. 자바는 Math또한 가지고있다 StrictMath.


언어 디자이너가 자동 완성 툴팁에 신경 쓰지 않기를 바랍니다. 또한 어떻게됩니까 Math.<^space>? 자동 완성 툴팁도 오염됩니다. 반대로, 나는 당신의 두 번째 단락이 아마도 더 나은 대답 중 하나라고 생각합니다.
Qix

@Qix 그들은 그렇습니다. 그러나 여기서 다른 사람들은 "인터페이스가 부풀어 오른다"고 할 수 있습니다.
Aleksandr Dubinsky

6

여기에는 흥미로운 대칭이 있다는 것을 올바르게 관찰했습니다.

내가 말 sqrt(n)하거나 n.sqrt()실제로 중요하지 않더라도, 그들은 같은 것을 표현하고 당신이 선호하는 것은 다른 것보다 개인적인 취향의 문제입니다.

그렇기 때문에 특정 언어 설계자들이 두 구문을 서로 바꾸어 놓아야한다는 강력한 논거가 있습니다. D 프로그래밍 언어는 이미 Uniform Function Call Syntax 라는 기능으로 이것을 허용합니다 . C ++에서 표준화를위한 비슷한 기능도 제안 되었습니다 . 으로 표시 Amery이 코멘트에 지적 , 파이썬은 너무이 가능합니다.

문제가없는 것은 아닙니다. 이와 같은 기본적인 구문 변경을 도입하면 기존 코드에 광범위한 영향을 미치며 물론 두 가지 구문을 다른 것을 설명하는 것으로 생각하도록 수십 년 동안 훈련 된 개발자들 사이에서 논란의 여지가있는 주제이기도합니다.

두 사람의 통일이 장기적으로 실현 가능한지 시간 만 알 수있을 것 같지만 확실히 흥미로운 고려 사항입니다.


파이썬은 이미이 두 가지 구문을 모두 지원합니다. 정적이 아닌 모든 메서드는 self첫 번째 매개 변수로 사용되며 클래스의 속성 대신 인스턴스의 속성으로 메서드를 호출하면 인스턴스가 암시 적으로 첫 번째 인수로 전달됩니다. 따라서 나는 쓸 수 있습니다 "foo".startswith("f")또는 str.startswith("foo", "f"), 내가 쓸 수 있습니다 my_list.append(x)또는 list.append(my_list, x).
Mark Amery

@MarkAmery 좋은 지적입니다. 이것은 D 또는 C ++ 제안서만큼이나 과감하지는 않지만 일반적인 아이디어에 적합합니다. 지적 해 주셔서 감사합니다!
ComicSansMS

3

D Stanley 의 답변 외에도 다형성에 대해 생각해야합니다. Math.Sqrt와 같은 메소드는 항상 동일한 값을 동일한 입력에 리턴해야합니다. 정적 메서드는 재정의 할 수 없으므로 메서드를 정적으로 만드는 것이이 점을 명확하게하는 좋은 방법입니다.

ToString () 방법을 언급했습니다. 여기서는이 메소드를 재정 의하여 (하위) 클래스를 다른 방식으로 부모 클래스로 표현할 수 있습니다. 따라서 인스턴스 메소드로 만듭니다.


2

Java에는 모든 기본 유형에 대한 래퍼가 있습니다.
기본 유형은 클래스 유형이 아니며 멤버 함수가 없습니다.

따라서 다음과 같은 선택 사항이 있습니다.

  1. 모든 도우미 함수를와 같은 형식 클래스로 수집하십시오 Math.
  2. 해당 랩퍼에서 정적 함수로 만드십시오.
  3. 해당 랩퍼에서 멤버 함수로 만드십시오.
  4. Java 규칙을 변경하십시오.

자바는 자바이고, 지지자들은 그것을 좋아한다고 공언하기 때문에 옵션 4를 배제하자.

객체 할당은 상당히 저렴하지만 무료가 아니며 반복해서 수행하는 것이 더해 지기 때문에 옵션 3을 배제 할 수도 있습니다.

두 아래, 여전히 하나 죽일 : 그것은 의미하기 때문에 옵션 2는, 또한 나쁜 생각 의 모든 기능이 구현되어야 모든 유형, 하나는 격차를 채우기 위해 변환을 확대에 의존 할 수없는, 또는 불일치가 정말 다치게됩니다.
를 살펴보면 특히 각각 보다 작은 유형의 경우 간격 java.lang.Math많이int 있습니다.double .

결국, 명백한 승자는 옵션 1이며, 유틸리티-기능-클래스에서 한 곳에서 모두 수집합니다.

옵션 4로 돌아가서, 그 방향의 무언가는 실제로 훨씬 나중에 발생했습니다. 이름을 매우 오랫동안 해결할 때 컴파일러가 원하는 클래스의 모든 정적 멤버를 고려하도록 요청할 수 있습니다. import static someclass.*;

또한 다른 언어에는 자유 기능에 대한 편견이 없거나 (선택적으로 네임 스페이스 사용) 훨씬 적은 수의 작은 유형이 있기 때문에 그러한 문제가 없습니다.


1
Math.min()모든 래퍼 유형에서 변형을 구현하는 기쁨을 고려하십시오 .

설득력이 # 4입니다. Math.sqrt()sqrt ()를 넣기로 결정했을 때 Math"그런 식으로 그것을 좋아하는"Java 사용자의 역사적 관성은 없었습니다. 에 큰 문제는 없지만 sqrt()오버로드 동작 Math.round()은 끔찍합니다. 유형의 값으로 회원 구문을 사용 할 수 있다는 floatdouble그 문제를 피할 수 있었을 것이다.
supercat 2019

2

내가 명시 적으로 언급하지 않은 한 가지 점은 (아몬이 암시한다고 주장하지만) 제곱근은 "유도 된"작업으로 생각할 수 있다는 것입니다. 구현이 우리에게 제공하지 않으면 직접 작성할 수 있습니다.

질문에 언어 디자인 태그가 붙어 있기 때문에 언어에 구애받지 않는 설명을 고려할 수 있습니다. 많은 언어가 다른 철학을 가지고 있지만 패러다임에서 캡슐화를 사용하여 불변을 보존하는 것은 매우 일반적입니다. 즉, 유형이 제안한대로 작동하지 않는 값을 피하는 것입니다.

예를 들어, 기계어를 사용하여 정수를 구현 한 경우 표현을 어떻게 든 캡슐화하고 싶을 수도 있습니다 (예 : 비트 시프트가 부호를 변경하지 못하도록 방지). 부가.

일부 언어는 클래스 및 개인 메소드로이를 구현할 수 있습니다.

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 확장 메서드와 같이 멤버 구문을 사용하거나 멤버 시퀀스의 변형을 사용하여 정적 메서드를 호출하는 것을 언어에서 막을 수는 없습니다 (더블 도트 구문을 보았을 것입니다. 주요 주장에 적합한 함수 만 나열 할 수있는 Intellisense의 장점을 허용하지만 실제 멤버 연산자와의 모호함을 피하십시오).
supercat 2019

-2

Java 및 C #에서 ToString은 클래스 계층의 루트 인 객체의 메소드이므로 모든 객체는 ToString 메소드를 구현합니다. Integertype의 경우 ToString 구현이 이런 식으로 작동하는 것이 당연합니다.

그래서 당신은 추론이 잘못되었습니다. 값 유형이 ToString을 구현하는 이유는 일부 사람들이 그런 것이 아니기 때문입니다. 값 유형에 대한 ToString 메소드를 사용합시다. ToString이 이미 있고 "가장 자연스러운"출력 결과이기 때문입니다.


1
물론 그것은 즉이하기로 결정, 결정이다 objectToString()방법을. 그것은 "일부 사람들은 다음과 같습니다 : 이봐, 값 유형에 대한 ToString 메소드를 보자"는 말입니다.
Residuum

-3

String.substring과 달리 Number.sqrt는 실제로 숫자의 속성이 아니라 숫자를 기반으로 한 새로운 결과입니다. 나는 당신의 숫자를 제곱 함수에 전달하는 것이 더 직관적이라고 생각합니다.

또한 Math 객체에는 다른 정적 멤버가 포함되어 있으므로 함께 묶어서 균일 한 방식으로 사용하는 것이 더 합리적입니다.


3
카운터 예 : BigInteger.pow () .
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.