왜 사용자 정의 연산자가 더 일반적이지 않습니까?


94

함수형 언어에서 놓칠 수있는 기능 중 하나는 연산자는 함수일 뿐이므로 사용자 지정 연산자를 추가하는 것은 함수를 추가하는 것만 큼 간단합니다. 많은 절차 적 언어는 연산자 오버로드를 허용하므로 어떤 의미에서 연산자는 여전히 함수입니다 (이것은 연산자가 템플릿 매개 변수에 문자열로 전달되는 D 에서 매우 그렇습니다 ).

연산자 오버로드가 허용되는 경우 사용자 지정 연산자를 추가하는 것이 쉽지 않은 경우가 많습니다. 나는 발견 이 블로그 게시물 사용자 정의 운영자가 있기 때문에 우선 순위 규칙의 중위 표기와 함께 잘 작동하지 않는 것을 주장하지만, 저자는이 문제에 대한 몇 가지 솔루션을 제공합니다.

주변을 둘러 보았고 해당 언어의 사용자 지정 연산자를 지원하는 절차 언어를 찾을 수 없었습니다. C ++의 매크로와 같은 해킹이 있지만 언어 지원과 거의 동일하지 않습니다.

이 기능은 구현하기가 쉽지 않기 때문에 왜 더 일반적이지 않습니까?

나는 그것이 추악한 코드로 이어질 수 있음을 이해하지만 과거에는 언어 디자이너가 쉽게 악용 될 수있는 유용한 기능 (매크로, 삼항 연산자, 안전하지 않은 포인터)을 추가하는 것을 막지 못했습니다.

실제 사용 사례 :

  • 누락 된 연산자 구현 (예 : Lua에는 비트 연산자가 없음)
  • D를 모방 ~(배열 연결)
  • DSL
  • 사용 |(코 루틴 / 발전기를 사용하여) 유닉스 파이프 스타일의 구문 설탕으로

또한 언어에 관심이 있어요 사용자 정의 연산자를 허용,하지만 난 더 관심이 있어요 이 제외되었습니다. 사용자 정의 연산자를 추가하기 위해 스크립팅 언어를 포크하는 것에 대해 생각했지만 어디에서 보지 못했다는 사실을 깨달았을 때 언어 디자이너가 나보다 똑똑한 이유가있을 수 있습니다.


4
있습니다 레딧 토론 이 질문에 대한 진행은.
동적

2
@ dimatura : R에 대해서는 많이 알지 못하지만 사용자 지정 연산자는 여전히 유연하지 않습니다 (예 : 고정도, 범위, 과부하 등을 정의하는 적절한 방법이 없음). 왜 그렇게 많이 사용되지 않는지 설명합니다. 그것은 다른 언어 들과는 달리, Haskell에서는 사용자 정의 접두사 연산자 를 많이 사용 합니다. 합리적으로 맞춤 중위 지원 절차 언어의 또 다른 예는 니므롯 , 물론 펄은 또한 그들을 수 있습니다 .
leftaroundabout

4
다른 옵션이 있습니다. Lisp는 실제로 연산자와 함수 사이에 차이가 없습니다.
BeardedO

4
프롤로그에 대한 언급은 아직 없습니다. 연산자는 함수에 대한 구문 설탕 일 뿐이며 (예 : 수학), 사용자 지정 연산자 사용자 정의 우선 순위 로 정의 할 수 있습니다 .
David Cowden

2
@BeardedO, Lisp에는 접두사 연산자가 전혀 없습니다. 당신이 그들을 소개하면, 당신은 우선 순위와 같은 모든 문제를 처리해야합니다.
SK-logic

답변:


134

프로그래밍 언어 디자인에는 두 개의 반대되는 생각의 학교가 있습니다. 하나는 프로그래머가 적은 제한으로 더 나은 코드를 작성하고 다른 하나는 더 많은 제한으로 더 나은 코드를 작성한다는 것입니다. 제 생각에는 경험이 풍부한 숙련 된 프로그래머가 제한이 적 으면서 번영 할 수 있지만 그 제한은 초보자의 코드 품질에 도움이 될 수 있습니다.

사용자 정의 연산자는 숙련 된 손에 매우 우아한 코드를 만들 수 있으며 초보자는 완전히 끔찍한 코드를 만들 수 있습니다. 따라서 언어에 언어가 포함되어 있는지 여부는 언어 디자이너의 사고 학교에 따라 다릅니다.


23
두 생각의 학교에서 언어 디자이너 가 하나를 다른 것을 선택 하는지 에 대한 질문에 가장 직접적이며 아마도 가장 진실한 답변을 얻은 것으로 plus.google.com/110981030061712822816/posts/KaSKeg4vQtz 도 +1했습니다 . 또한 여러분의 논문을 바탕으로 더 적은 수의 언어로 더 적은 수의 개발자가 다른 것보다 숙련되어 있기 때문에 더 적은 수의 언어로 허용 할 수 있다고 말할 수 있습니다 (최상의 퍼센트는 항상 정의상 소수입니다)
Jimmy Hoffa

12
이 답변은 모호하며 질문과는 거의 관련이 없다고 생각합니다. (I는 또한 나의 경험 모순하지만 그건 중요하지 않아 이후 특성이 잘못이라고 생각하는 일.)
콘라드 루돌프에게

1
@KonradRudolph가 말했듯이 이것은 실제로 질문에 대답하지 않습니다. Prolog와 같은 언어를 사용하면 접두사 또는 접두사를 배치 할 때 우선 순위를 정의하는 것을 포함하여 연산자로 원하는 모든 작업을 수행 할 수 있습니다. 사용자 정의 연산자를 작성할 수 있다는 사실은 대상 독자의 기술 수준과 관련이 있다고 생각하지 않지만 Prolog의 목표는 가능한 한 논리적으로 읽는 것입니다. 사용자 정의 연산자를 포함하면 매우 논리적으로 읽는 프로그램을 작성할 수 있습니다 (모든 프롤로그 프로그램은 여러 논리 문일뿐입니다).
David Cowden

그 스티브 랜트를 어떻게 그리웠습니까? Oh man, bookmarked
George Mauer

컴파일러가 다양한 추론을해야 할 때 (예 : Format메소드 에 대한 자동 복싱 인수 ) 언제 거부 해야하는지 말할 수있는 도구를 언어에 제공하면 프로그래머가 가장 좋은 코드를 작성할 가능성이 가장 높다고 생각하면 어떤 철학에 빠질 것인가? 예를 들어)에 대한 자동 권투 인수 ReferenceEquals. 언어가 프로그래머가 특정 추론이 부적절 할 때 말할 수있는 능력이 많을수록 적절한 경우 편리한 추론을 제공 할 수 있습니다.
supercat

83

~ 또는 "myArray.Concat (secondArray)"를 사용하여 연결하는 배열 중에서 선택하면 후자를 선호 할 것입니다. 왜? ~는 완전히 의미가없는 문자이기 때문에 작성된 특정 프로젝트에서 주어진 의미 (배열 연결의 의미) 만 가지고 있습니다.

기본적으로 말했듯이 연산자는 메소드와 다르지 않습니다. 그러나 코드 흐름을 이해하는 데 도움이되는 읽기 쉽고 이해하기 쉬운 이름을 메서드에 제공 할 수 있지만 연산자는 불투명하고 상황에 따라 다릅니다.

그렇기 때문에 필자는 PHP .연산자 (문자열 연결) 나 Haskell 또는 OCaml의 대부분 연산자를 좋아하지 않지만이 경우 기능 언어에 대해 보편적으로 인정되는 표준이 등장하고 있습니다.


27
모든 운영자에 대해서도 마찬가지입니다. 그것들을 좋게 만들고 사용자 정의 연산자를 좋게 만들 수있는 이유는 다음과 같습니다. 문자로 사용되는 문자에도 불구하고 널리 사용되며 니모닉 속성은 소스 코드에서 즉시 의미를 갖습니다. 그것에 익숙한 사람들에게는 분명합니다. 따라서 귀하의 답변은 설득력있는 IMHO가 아닙니다.

33
마지막에 언급했듯이 일부 연산자는 표준 산술 기호, 비트 이동 및 AND / OR 등의 보편적 인 인식 기능을 가지고 있기 때문에 그 간결함이 불투명도를 능가합니다. 그러나 임의의 연산자 를 정의 할 수 있다는 것은 두 세계에서 최악을 유지하는 것으로 보입니다.
Avner Shahar-Kashtan

11
예를 들어 언어 수준에서 단일 문자 이름을 금지하는 데 유리한가요? 더 일반적으로, 좋은 것보다 더 해로운 것으로 보이는 언어의 어떤 것도 허용하지 않습니까? 그것은 언어 디자인에 대한 올바른 접근 방법이지만 유일한 것은 아닙니다. 따라서 그것을 전제 할 수는 없다고 생각합니다.

9
사용자 지정 연산자가 기능을 "추가"하고 제한을 제거한다는 데 동의하지 않습니다. 연산자는 일반적으로 함수일 뿐이므로 연산자에 대한 정적 심볼 테이블 대신 상황에 맞는 동적 컨텍스트 테이블을 대신 사용할 수 있습니다. 나는 때문에,이 연산자의 오버로드를 처리하는 방법을 상상 +하고 <<확실하게 정의되지 않은 Object(내가 "연산자에 대한 +에서 일치 ..."C에 노출 된 클래스에 그렇게 ++ 얻을).
beatgammit

10
계산하는 데 오랜 시간이 걸리는 것은 코딩이 일반적으로 컴퓨터가 사용자를 위해 무언가를하게하는 것이 아니라 사람과 컴퓨터가 읽을 수있는 문서를 생성하는 것입니다. 사람들은 컴퓨터보다 훨씬 더 중요합니다. 이 대답은 정확히 맞습니다. 다음 사람 (또는 2 년 후)이 ~ 의미를 알아 내려고 10 초를 소비 해야하는 경우 대신 메소드 호출에 10 초를 입력하는 데 소비했을 수도 있습니다.
Bill K

71

이 기능은 구현하기가 쉽지 않기 때문에 왜 더 일반적이지 않습니까?

당신의 전제는 잘못되었습니다. 그것은이다 없다 "구현 꽤 사소한". 실제로 많은 문제가 발생합니다.

게시물에서 제안 된 "솔루션"을 살펴 보겠습니다.

  • 우선 순위가 없습니다 . 저자 자신은“우선 순위 규칙을 사용하지 않는 것은 단순히 옵션이 아닙니다”라고 말합니다.
  • 시맨틱 인식 파싱 . 이 기사에서 알 수 있듯이 컴파일러에는 많은 의미 지식이 있어야합니다. 이 기사는 실제로 이것에 대한 해결책을 제공하지 않으며 말해 줄 것입니다. 이것은 간단하지 않습니다. 컴파일러는 전력과 복잡성 간의 균형을 유지하도록 설계되었습니다. 특히 저자는 관련 정보를 수집하기위한 사전 구문 분석 단계를 언급하지만 사전 구문 분석은 비효율적이며 컴파일러는 구문 분석 패스를 최소화하기 위해 매우 열심히 노력합니다.
  • 사용자 정의 접두사 연산자가 없습니다 . 글쎄, 그건 해결책이 아니다.
  • 하이브리드 솔루션 . 이 솔루션에는 의미 인식 구문 분석의 많은 단점이 있지만 전부는 아닙니다. 특히, 컴파일러는 알려지지 않은 토큰을 잠재적으로 사용자 지정 연산자를 나타내는 것으로 취급해야하기 때문에 종종 의미있는 오류 메시지를 생성 할 수 없습니다. 또한, 파싱을 계속 진행하기 위해 (유형 정보 등을 수집하기 위해) 상기 연산자의 정의를 요구할 수 있으며, 추가적인 파싱 패스가 다시 필요하다.

대체로 이것은 파서의 복잡성과 성능 측면에서 구현하기위한 고가의 기능이며, 많은 이점을 가져올 지 확실하지 않습니다. 물론, 새로운 연산자를 정의하는 능력 에는 몇 가지 이점이 있지만 논쟁의 여지가 있습니다 (새로운 연산자를 갖는 것이 좋지 않다고 주장하는 다른 답변을보십시오).


2
정신의 소리에 감사드립니다. 내 경험에 따르면 사소한 일을 해소하는 사람들 은 전문가이거나 행복하게 무지한 사람이며 후자는 더 자주 후자입니다. : x
Matthieu M.

14
이 문제들 중 하나 하나 하나가 20 년 동안 존재 해 온 언어로 해결되었습니다.
Philip JF

11
그럼에도 불구하고 구현하는 것은 매우 사소한 일입니다. 모든 드래곤 북 파싱 알고리즘은 잊어 버리십시오. 21 세기이며 계속 진행할 때입니다. 언어 구문 자체를 확장하는 것조차 쉽지만 주어진 우선 순위의 연산자를 추가하는 것은 쉽지 않습니다. Haskell 파서를 살펴보십시오. "주류"bloated 언어의 파서보다 훨씬 간단합니다.
SK-logic

3
@ SK-logic 당신의 담요 주장은 저를 설득하지 않습니다. 예, 파싱이 진행되었습니다. 아니요, 유형에 따라 임의의 연산자 우선 순위를 구현하는 것은 "사소한"것이 아닙니다. 제한된 컨텍스트로 좋은 오류 메시지를 생성하는 것은 "사소한"것이 아닙니다. 변환하기 위해 여러 패스가 필요한 언어로 효율적인 파서를 생성하는 것은 불가능합니다.
Konrad Rudolph

6
Haskell에서 파싱은 "쉽습니다"(대부분의 언어와 비교). 우선 순위는 컨텍스트와 무관합니다. 하드 오류 메시지를 제공하는 부분은 사용자 정의 연산자가 아니라 형식 클래스 및 고급 형식 시스템 기능과 관련이 있습니다. 사용자 정의가 아닌 오버로드이며 이는 어려운 문제입니다.
Philip JF

25

현재 "운영자가 가독성에 해를 입히기 위해 학대를 당한다"는 주장을 무시하고 언어 설계에 영향을 주도록하자.

접두사 연산자는 단순한 우선 순위 규칙보다 더 많은 문제를 가지고 있습니다 (무딘 것은 아니지만 참조 링크는 해당 디자인 결정의 영향을 무시합니다). 당신이 정의 할 때 어떤 일이 발생 : 하나는 갈등 해상도 a.operator+(b)b.operator+(a)? 다른 것을 선호하면 해당 연산자의 예상되는 교환 속성이 ​​손상됩니다. 오류가 발생하면 작동하지 않는 모듈이 함께 한 번 고장날 수 있습니다. 파생 된 형식을 믹스에 던지기 시작하면 어떻게됩니까?

문제는 연산자가 단순한 기능이 아니라는 것입니다. 함수는 독립형이거나 해당 클래스에서 소유합니다.이 함수는 다형성 디스패치를 ​​소유 한 매개 변수 (있는 경우)에 대한 명확한 기본 설정을 제공합니다.

그리고 이는 운영자로부터 발생하는 다양한 패키징 및 해결 문제를 무시합니다. 언어 디자이너가 전반적으로 연산자의 정의를 제한하는 이유는 논쟁의 여지가있는 이점을 제공하면서 언어에 많은 문제를 야기하기 때문입니다.

그들이이기 때문에 솔직히 하지 사소한 구현합니다.


1
이것이 Java에 포함되지 않은 이유라고 생각하지만 언어가있는 언어는 우선 순위에 대한 명확한 규칙이 있습니다. 매트릭스 곱셈과 비슷하다고 생각합니다. 정식이 아니므로 행렬을 사용할 때 알고 있어야합니다. 나는 수업을 다룰 때도 똑같이 적용된다고 생각하지만, 나는 비정규적인 +것이 악하다 는 것에 동의합니다 . 그러나 이것이 실제로 사용자 정의 연산자에 대한 논쟁입니까? 일반적으로 연산자 오버로드에 대한 논쟁처럼 보입니다.
beatgammit

1
@tjameson-그렇습니다. 언어에는 수학에 대한 명확한 우선 순위 규칙이 있습니다 . 연산자에 과부하가 걸리면 수학 연산에 대한 우선 순위 규칙이 실제로 적용되지 않을 수 boost::spirit있습니다. 수학에 대한 우선 순위를 잘 정의 할 수있는 좋은 방법이 없기 때문에 사용자 정의 연산자가 더 악화되는 것을 허용하자마자. 나는 임의로 정의 된 연산자의 문제를 구체적으로 다루는 언어의 맥락에서 저 자신에 대해 조금 썼습니다 .
Telastyn

22
나는 헛소리라고 부릅니다. OO-ghetto 외부에는 생명이 있으며 함수가 반드시 어떤 객체에 속할 필요는 없으므로 올바른 함수를 찾는 것은 올바른 연산자를 찾는 것과 정확히 같습니다.
Matthieu M.

1
matt -물론 이죠 회원 기능을 지원하지 않는 언어에서는 소유권 및 발송 규칙이 더 균일합니다. 하지만 원래의 질문은 왜 비 OO 언어가 더 일반적이지 않습니까? 이것은 다른 전체 볼 게임입니다.
Telastyn

11
Haskell이 사용자 정의 연산자를 어떻게 수행하는지 살펴 보셨습니까? 또한 관련된 우선 순위가 있다는 점을 제외하고는 일반 기능과 똑같이 작동 합니다 . (실제로 일반 기능도 마찬가지이므로 실제 차이도 없습니다.) 기본적으로 연산자는 기본적으로 접두사이고 이름은 접두사이지만 유일한 차이점입니다.
Tikhon Jelvis

19

운영자 오버로드 어떤 형태 구현 되는가에 놀랄 것 입니다. 그러나 많은 커뮤니티에서 일반적으로 사용되지는 않습니다.

왜 ~를 사용하여 배열에 연결합니까? Ruby처럼 <<를 사용 하지 않는 이유 무엇입니까? 당신이 일하는 프로그래머는 아마도 루비 프로그래머가 아닐 것입니다. 또는 D 프로그래머. 그들이 코드를 발견 할 때 무엇을합니까? 그들은 상징이 무엇을 의미하는지 찾아야합니다.

나는 기능적 언어에 대한 맛을 가진 아주 훌륭한 C # 개발자와 함께 일했었다. 그는 확장 방법과 표준 모나드 용어를 사용하여 모나드 를 C #에 도입하기 시작했습니다 . 아무도 자신의 코드가 의미가 무엇인지 알면 더 간결하고 더 읽기 쉽다고 주장 할 수는 없었지만 , 코드가 이해되기 전에 모든 사람이 모나드 용어를 배워야 한다는 것을 의미했습니다 .

충분하다고 생각하십니까? 작은 팀이었습니다. 개인적으로 동의하지 않습니다. 모든 새로운 개발자는이 용어에 혼란을 겪게되었습니다. 새로운 도메인을 배우는데 문제가 충분하지 않습니까?

반면에, 다른 C # 개발자들은 그것이 무엇인지 알기를 기대하기 때문에 C # 에서 ??연산자 를 행복하게 사용할 것입니다. 그러나 기본적으로 지원하지 않는 언어로 오버로드하지는 않을 것입니다.


Double.NaN 예제를 이해하지 못합니다.이 동작은 부동 소수점 사양의 일부이며 내가 사용한 모든 언어에서 지원됩니다. 사용자 정의 연산자는 기능에 대해 모르는 개발자를 혼동시키기 때문에 지원되지 않는다고 말하는가? 삼항 연산자 또는 ??예제를 사용하는 것에 대한 동일한 주장처럼 들립니다 .
beatgammit

@ tjameson : 사실 당신이 맞습니다. 실제로 내가 작성하려고 한 점에 약간 접하고 있었으며 특히 잘 작성되지 않았습니다. 나는 생각했다 ?? 예를 들어서 그 예제를 선호합니다. double.NaN 단락을 제거합니다.
pdr

6
"모든 새로운 개발자가이 용어로 혼동 할 운명"이라고 생각합니다. 새로운 개발자가 코드베이스에 대한 학습 곡선을 걱정하는 것은 새로운 개발자의 학습 곡선을 걱정하는 것과 같습니다. .NET의 새로운 버전. 개발자가 그것을 배울 때 (새로운 .NET 또는 코드베이스) 더 중요하다고 생각합니다. 그렇다면 코드는 셀 수없이 많은 시간을 처리해야하기 때문에 각 개발자가 한 번만 학습하면되므로 학습 곡선은 그만한 가치가 있습니다. 따라서 코드를 개선 할 경우 비용이 약간 듭니다.
Jimmy Hoffa

7
@JimmyHoffa 반복되는 비용입니다. 모든 새로운 개발자에게 선불로 지불하고 지원해야하기 때문에 기존 개발자에게도 영향을 미칩니다. 문서화 비용과 위험 요소가 있습니다. 오늘날 충분히 안전하다고 느끼지만 30 년이 지나면 모든 사람들이 움직일 것입니다. 언어와 응용 프로그램이 이제는 "레거시"입니다. ".concat ()"을 입력하기에는 너무 게으른 프로그래머의 광채로 일부 나쁜 빨판이 머리를 찢어 버릴 것입니다. 예상 가치가 비용을 상쇄하기에 충분합니까?
Sylverdrag

3
또한 "모든 새로운 개발자는이 용어로 인해 혼란스러워 질 것입니다. 새로운 도메인을 배우는데 충분한 문제가 있습니까?" 80 년대의 OOP, 그 이전의 구조화 된 프로그래밍, 또는 10 년 전의 IoC에 적용되었을 수 있습니다. 이 잘못된 주장의 사용을 중단 할 때입니다.
Mauricio Scheffer

11

몇 가지 이유를 생각할 수 있습니다.

  • 그것들 은 구현하기가 쉽지 않습니다 -임의의 커스텀 연산자를 사용하면 컴파일러를 훨씬 더 복잡하게 만들 수 있습니다. 특히 사용자 정의 우선 순위, 고 정성 및 arity 규칙을 허용하는 경우. 단순함이 미덕이라면, 연산자 오버로딩은 훌륭한 언어 디자인에서 벗어나게합니다.
  • 그들은 연산자를 재정의하고 모든 종류의 사용자 정의 클래스에 대해 재정의를 시작하는 것이 "쿨"하다고 생각하는 코더들에 의해 학대 당 합니다. 오래지 않아, 코드는 운영자가 기존의 잘 이해 된 규칙을 따르지 않기 때문에 아무도 읽거나 이해할 수없는 많은 사용자 정의 기호로 가득 차 있습니다. DSL이 수학의 하위 집합이 아닌 한 "DSL"인수를 구입하지 않습니다.
  • 가독성과 유지 보수성손상 됩니다. 운영자가 정기적으로 재정의되는 경우이 시설을 사용할 때 발견하기가 어려워 질 수 있으며 코더는 운영자가 무엇을하고 있는지 지속적으로 질문해야합니다. 의미있는 기능 이름을 지정하는 것이 훨씬 좋습니다. 몇 가지 추가 문자를 입력하면 저렴하고 장기적인 유지 관리 문제가 발생합니다.
  • 암시 적 성능 기대를 깰 수 있습니다 . 예를 들어, 일반적으로 배열의 요소 조회는 O(1)입니다. 그러나 연산자 오버로딩 을 사용하면 인덱싱 연산자의 구현에 따라 someobject[i]쉽게 O(n)작업이 될 수 있습니다 .

실제로, 일반 기능을 사용하는 것보다 운영자 과부하가 정당한 용도로 사용되는 경우는 거의 없습니다. 합법적 인 예는 수학적 연산자가 복소수에 대해 정의 된 잘 이해 된 방법을 이해하는 수학자가 사용할 복소수 클래스를 설계하는 것일 수 있습니다. 그러나 이것은 실제로는 흔한 일이 아닙니다.

고려해야 할 몇 가지 흥미로운 사례 :

  • 리스프 : 일반적으로 연산자와 함수를 전혀 구분하지는 않습니다 +. 단지 일반적인 함수입니다. +연산자를 포함하여 원하는대로 함수를 정의 할 수 있습니다 (일반적으로 내장 함수와의 충돌을 피하기 위해 별도의 네임 스페이스에 정의하는 방법이 있습니다 ). 그러나 의미있는 기능 이름을 사용하는 문화적 경향이 있으므로, 이는 많이 남용되지 않습니다. 또한 Lisp 접두사 표기법은 독점적으로 사용되는 경향이 있으므로 "구문 설탕"에는 운영자 과부하가 제공하는 값이 적습니다.
  • 자바 - 연산자 오버로딩을 허용하지 않습니다. 이것은 때때로 성가신 일이지만 (복합 수 사례와 같은 경우) 평균적으로 단순한 범용 OOP 언어로 의도 된 Java에 대한 올바른 디자인 결정 일 것입니다. Java 코드는 저 / 중급 기술 개발자가이 단순성의 결과로 유지 관리하기가 실제로 쉽습니다.
  • C ++ 에는 매우 복잡한 연산자 오버로드가 있습니다. 때로는 이것이 악용 될 수 cout << "Hello World!"있지만 ( 누구나?) C ++의 위치는 복잡한 언어로서 고급 프로그래밍을 가능하게하고 성능을 위해 금속에 매우 가까워 질 수 있도록하는 복잡한 언어로 접근하는 것이 합리적입니다. 성능 저하없이 원하는대로 정확하게. 발로 자신을 쏠 경우 자신의 책임임을 이해합니다.

8

이 기능은 구현하기가 쉽지 않기 때문에 왜 더 일반적이지 않습니까?

아니다 (사소하게 구현하지 않는 한)를 구현하는 사소한. 또한 이상적으로 구현 된 경우에도 크게 도움이되지 않습니다. 간결함에서 얻을 수있는 가독성은 친숙 함과 불투명도로 인한 가독성 손실로 상쇄됩니다. 간단히 말해 개발자 나 사용자의 시간에 가치가 없기 때문에 드문 일입니다.

즉, 나는 그것을하는 세 가지 언어를 생각할 수 있으며 다른 방식으로 수행합니다.

  • 구성표가 아닌 라켓 인 S-expression-y는 확장하려는 구문에 대해 구문 분석기로 얼마만큼의 구문을 작성해야하는지 (및이를 다루기 쉽게 만드는 유용한 후크를 제공함) 기대합니다.
  • 순전히 함수 프로그래밍 언어 인 Haskell은 구두점만으로 구성되는 모든 연산자를 정의 할 수 있으며 고정도 (10)와 연관성을 제공 할 수 있습니다. 이진 연산자와 고차 함수로 삼차 등 연산자를 만들 수 있습니다.
  • 의존적으로 유형이 지정된 프로그래밍 언어 인 Agda는 if-then 및 if-then-else를 동일한 프로그램에서 연산자로 정의 할 수 있도록 연산자 ( 여기서는 여기에 있음 )를 통해 매우 유연 하지만 어휘 분석기, 구문 분석기 및 평가 기는 모두 강력하게 결합되어 있습니다. 결과적으로.

4
당신은 "사소하지 않다"고 말했고, 사소한 구현을 특징으로하는 세 가지 언어를 즉시 나열했습니다. 결국, 그것은 사소한 것이지 않습니까?
SK-logic

7

사용자 지정 연산자가 권장되지 않는 주된 이유 중 하나는 모든 연산자가 무엇이든 할 수 있기 때문입니다.

예를 들어, cstream왼쪽 비틀림 과부하가 많은 비판을 받았습니다.

언어가 연산자 오버로드를 허용하는 경우 일반적으로 혼동을 피하기 위해 연산자 동작을 기본 동작과 유사하게 유지하는 것이 좋습니다.

또한 사용자 정의 연산자는 특히 사용자 지정 기본 설정 규칙이있는 경우 구문 분석을 훨씬 어렵게 만듭니다.


6
연산자 과부하에 대한 부분이 완전히 새로운 연산자를 정의하는 데 어떻게 적용되는지 알 수 없습니다.

나는 연산자 과부하 (선형 대수 라이브러리)를 매우 잘 사용하고 언급 한 것처럼 매우 나빴습니다. 나는 이것이 커스텀 연산자에 대한 좋은 주장이라고 생각하지 않습니다. 구문 분석은 문제가 될 수 있지만 연산자가 예상되는 경우에는 분명합니다. 파싱 ​​후 연산자 의미를 찾는 것은 코드 생성기에 달려 있습니다.
beatgammit

3
그리고 이것이 메소드 오버로드와 어떻게 다릅니 까?
Jörg W Mittag

메소드 오버로드가있는 @ JörgWMittag에는 의미있는 이름이 붙어 있습니다. 기호로 어떤 일이 일어날지를 간결하게 설명하기가 더 어렵다
래칫 괴물

1
@ratchetfreak : 글쎄. +두 가지를 -더하고 빼고 *곱합니다. 내 생각에 아무도 프로그래머가 함수 / 메소드를 add실제로 만들거나 강제로 doNothing핵무기를 시작할 수 없다고 생각한다 . 그리고 a.plus(b.minus(c.times(d)).times(e)읽기가 훨씬 어렵습니다 a + (b - c * d) * e(추가 보너스-첫 번째 찌르기에서 전사 오류입니다). 첫 번째가 더 의미있는 방법을
모르겠습니다

4

사용자 정의 단어를 사용하지 않는 것과 같은 이유로 사용자 정의 연산자를 사용하지 않습니다. 아무도 그들의 기능을 "sworp"라고 부르지 않을 것입니다. 다른 사람에게 생각을 전달할 수있는 유일한 방법은 공유 언어를 사용하는 것입니다. 이는 코드를 작성하는 사회에 단어와 기호 (연산자)를 모두 알고 있어야한다는 것을 의미합니다.

따라서 프로그래밍 언어에서 사용하는 연산자는 학교에서 산수 (산술)이거나 프로그래밍 커뮤니티에서 설정된 연산자 (예 : 부울 연산자)입니다.


1
함수 뒤에 의미를 발견하는 능력을 추가하십시오. "sworp"의 경우 검색 창에 쉽게 넣을 수 있고 문서를 찾을 수 있습니다. 운영자를 검색 엔진에 집어 넣는 것은 완전히 다른 볼 파크입니다. 현재 검색 엔진은 운영자를 찾는 데 어려움을 겪고 있으며 경우에 따라 의미를 찾기 위해 많은 시간을 낭비했습니다.
Mark H

1
"학교"는 얼마입니까? 예를 들어, 나는 ∈ for elem가 좋은 아이디어 라고 생각하며 확실히 모든 사람이 이해해야하지만 다른 사람들은 동의하지 않는 것 같습니다.
Tikhon Jelvis 2:31에

1
기호에는 문제가 없습니다. 키보드의 문제입니다. 예를 들어 유니 코드 beautifier가 활성화 된 emacs haskell-mode를 사용합니다. 또한 ASCII 연산자를 유니 코드 기호로 자동 변환합니다. 그러나 ASCII 동등 물이 없으면 입력 할 수 없습니다.
Vagif Verdi

2
자연어는 "사용자 정의 단어"만으로 만들어졌으며 다른 것은 없습니다. 그리고 일부 언어는 실제로 사용자 정의 단어 형성을 권장합니다.
SK-logic

4

이러한 오버로드를 지원하는 언어와 관련하여 스칼라는 실제로 C ++보다 훨씬 깨끗하고 더 나은 방법을 제공합니다. 대부분의 문자는 함수 이름에 사용할 수 있으므로 원하는 경우! + * = ++와 같은 연산자를 정의 할 수 있습니다. 내장 된 infix 지원 (하나의 인수를 취하는 모든 함수에 대해)이 있습니다. 그런 기능의 연관성을 정의 할 수 있다고 생각합니다. 그러나 우선 순위를 정의 할 수는 없습니다 (추악한 트릭 만 사용하십시오. 여기 참조 ).


4

아직 언급되지 않은 것 중 하나는 스몰 토크 (Smalltalk)의 경우인데, 여기에는 모든 연산자 (연산자 포함)가 메시지 전송입니다. "운영자"와 같은 +, |등등과 실제로 단항 방법이 있습니다.

모든 메서드를 재정의 할 수 있으므로 a + b정수 ab정수가 모두 정수인 경우 정수 덧셈을 의미 하고 둘 다 정수이면 벡터 덧셈을 의미 OrderedCollection합니다.

우선 순위 규칙은 메소드 호출 일 뿐이므로 없습니다. 이 표준 수학 표기법을위한 중요한 의미가 3 + 4 * 5수단 (3 + 4) * 5,하지 3 + (4 * 5).

(이것은 스몰 토크 초보자들에게는 큰 걸림돌입니다. 수학 규칙을 어기는 것은 특별한 경우를 제거하므로 모든 코드 평가가 왼쪽에서 오른쪽으로 균일하게 진행되어 언어가 훨씬 간단 해집니다.)


3

여기서 두 가지에 맞서 싸우고 있습니다.

  1. 왜 연산자가 언어로 존재합니까?
  2. 함수 / 메소드에 대한 연산자의 미덕은 무엇입니까?

대부분의 언어에서 연산자는 실제로 간단한 기능으로 구현되지 않습니다. 함수 스캐 폴딩이있을 수 있지만 컴파일러 / 런타임은 시맨틱 의미와이를 기계 코드로 효율적으로 변환하는 방법을 명시 적으로 알고 있습니다. 내장 함수와 비교할 때 훨씬 더 그렇습니다 (대부분의 구현에서는 구현에 모든 함수 호출 오버 헤드가 포함되지 않습니다). 대부분의 연산자는 CPU에서 발견되는 기본 명령어에 대한 상위 레벨 추상화입니다 (이는 대부분의 연산자가 산술, 부울 또는 비트 단위 인 이유입니다). 그것들을 "특별한"함수 ( "primitives"또는 "builtins"또는 "native"또는 다른 것)로 모델링 할 수 있지만,이를 위해서는 일반적으로 그러한 특수 함수를 정의하기위한 매우 강력한 의미론이 필요합니다. 대안은 의미 적으로 사용자 정의 연산자처럼 보이지만 컴파일러에서 특수 경로를 호출하는 내장 연산자를 사용하는 것입니다. 그것은 두 번째 질문에 대한 대답을 무시하고 실행됩니다 ...

위에서 언급 한 기계 번역 문제 외에도 구문 수준의 연산자는 기능과 크게 다르지 않습니다. 그들은 특징이 간결하고 상징적 인 경향이 있다는 점을 구별하고 있으며, 이는 유용한 추가 특징을 암시해야합니다. 개발자에게 의미 / 의미를 광범위하게 이해해야합니다. 짧은 기호는 이미 이해 된 일련의 의미론에 대한 짧은 손이 아닌 한 많은 의미를 전달하지 않습니다. 따라서 사용자 정의 연산자는 본질적으로 그다지 널리 이해되지 않기 때문에 본질적으로 도움이되지 않습니다. 하나 또는 두 개의 문자 기능 이름만큼 의미가 있습니다.

C ++의 연산자 오버로드는이를 검사하기위한 비옥 한 토대를 제공합니다. 대부분의 운영자 과부하 "남용"은 광범위하게 이해되는 일부 시맨틱 계약을 위반하는 과부하의 형태로 제공됩니다 (일반적인 예는 a + b! = b + a 또는 +가 피연산자).

연산자 오버로드 사용자 정의 연산자를 허용하는 스몰 토크를 보면 언어가 어떻게 작동하는지, 그리고 그것이 얼마나 유용한 지 알 수 있습니다. 스몰 토크에서 연산자는 구문 특성이 다른 메소드 일뿐입니다 (즉, 이진으로 인코딩됩니다). 이 언어는 특수 가속 연산자 및 방법에 "기본 방법"을 사용합니다. 사용자 정의 연산자가 작성되는 경우는 거의 없으며, 작성했을 때 작성자가 의도 한대로 사용하지 않는 경향이 있습니다. 연산자 오버로드에 해당하는 것조차 드물다. 왜냐하면 후자는 함수의 의미론의 표현을 허용하기 때문에 메소드 대신 새 함수를 메소드 대신 연산자로 정의하는 것이 순 손실이기 때문이다.


1

필자는 항상 C ++에서 연산자 오버로드가 단일 개발자 팀에게 편리한 지름길 인 것을 발견했지만, 메서드 호출이 쉽지 않은 방식으로 "숨겨져"있기 때문에 장기적으로 모든 종류의 혼란을 야기합니다. 독소와 같은 도구는 따로 분리해야하고 사람들은 관용구를 제대로 활용하기 위해 관용구를 이해해야합니다.

때로는 심지어 예상보다 이해하기가 훨씬 어렵습니다. 옛날 옛적에 크로스 플랫폼 C ++ 프로젝트에서 나는 FilePath객체 (Java File객체 와 유사)를 생성하여 경로가 작성되는 방식을 표준화하는 것이 좋겠다고 결정했다. 경로 부분 (따라서 File::getHomeDir()/"foo"/"bar"지원되는 모든 플랫폼에서 올바른 작업을 수행 할 수 있습니다). 그것을 본 모든 사람들은 본질적으로 "도대체 뭐야? 끈 구분?"

마찬가지로 그래픽스 프로그래밍이나 벡터 / 매트릭스 수학이 Matrix * Matrix, Vector * Vector (dot), Vector % Vector (cross), Matrix * Vector ( 행렬 변환), Matrix ^ Vector (균일 좌표를 무시하는 특수 행렬 행렬 변환-표면 법선에 유용) 등이 있지만 벡터 수학 라이브러리를 작성한 사람의 파싱 시간이 약간 절약되지만 종료됩니다. 다른 사람들을 위해 문제를 더 혼란스럽게 만듭니다. 그만한 가치가 없습니다.


0

연산자 오버로드는 메소드 오버로드가 나쁜 아이디어 인 것과 같은 이유로 나쁜 아이디어입니다. 화면의 동일한 기호는 주위의 환경에 따라 다른 의미를 갖습니다. 이로 인해 일상적인 읽기가 더 어려워집니다.

가독성은 유지 관리의 중요한 측면이기 때문에 항상 과부하를 피해야합니다 (일부 매우 특별한 경우 제외). 각 기호 (연산자 또는 영숫자 식별자)가 고유 한 의미를 갖는 것이 훨씬 좋습니다.

예를 들어, 익숙하지 않은 코드를 읽을 때 모르는 새로운 영숫자 식별자를 발견하면 적어도 모르는 이점이 있습니다. 그런 다음 찾아 볼 수 있습니다. 그러나 그 의미를 알고있는 공통 식별자 또는 연산자가 표시되면 실제로는 완전히 다른 의미로 오버로드 된 것을 알 수 없습니다. 오버로드를 광범위하게 사용하는 코드 기반에서 어떤 연산자가 오버로드되었는지 확인하려면 코드의 일부만 읽으려는 경우에도 전체 코드에 대한 실무 지식이 필요합니다. 이로 인해 새로운 개발자가 해당 코드를 빠르게 익히지 못하고 소규모 작업을 위해 사람들을 데려 올 수 없습니다. 이것은 프로그래머의 직업 보안에 도움이 될 수 있지만, 코드베이스의 성공에 대한 책임이있는 경우 모든 비용으로이 관행을 피해야합니다.

연산자의 크기가 작기 때문에 오버로드 연산자는 더 조밀 한 코드를 허용하지만 코드를 조밀하게 만드는 것은 실제 이점이 아닙니다. 로직이 두 배인 행은 읽는 데 두 배가 걸립니다. 컴파일러는 신경 쓰지 않습니다. 유일한 문제는 인간의 가독성입니다. 코드를 콤팩트하게 만들면 가독성이 향상되지 않으므로 콤팩트함에는 실질적인 이점이 없습니다. 계속해서 공간을 차지하고 고유 한 작업에 고유 한 식별자를 지정하면 장기적으로 코드가 더 성공할 것입니다.


"화면상의 동일한 기호는 주위의 환경에 따라 다른 의미를 갖습니다."-대부분의 언어에서 많은 연산자가 이미 그렇습니다.
rkj

-1

우선 순위와 복잡한 구문 분석을 다루기위한 기술적 어려움은 고려해야 할 프로그래밍 언어의 몇 가지 측면이 있다고 생각합니다.

연산자는 일반적으로 핵심 언어로 잘 정의되고 문서화 된 짧은 논리적 구조입니다 (비교, 할당 ..). 또한 (비교 문서없이 이해하기 일반적으로 어려운 a^bxor(a,b)예를 들면). 일반 프로그래밍 (>, <, =, + 등)에서 실제로 의미가있는 연산자는 다소 제한되어 있습니다.

내 생각은 언어로 잘 정의 된 연산자 세트를 고수하는 것이 좋습니다. 그런 다음 연산자에 연산자 오버로드를 허용하십시오 (연산자가 동일한 작업을 수행해야하지만 사용자 정의 데이터 유형을 사용하도록 권장합니다).

의 사용 사례 ~|실제로 간단한 연산자 오버로딩 (C #을, C ++ 등)이 가능하다. DSL은 유효한 사용 영역이지만 아마도 유일하게 유효한 영역 중 하나 일 것입니다. 그러나 새로운 언어를 만드는 더 좋은 도구가 있다고 생각합니다. 다른 컴파일러 내에서 진정한 DSL 언어를 실행하는 것은 컴파일러 컴파일러 도구를 사용하는 것이 어렵지 않습니다. "확장 LUA 인수"도 마찬가지입니다. 언어는 주로 하위 언어의 기초가 아닌 특정 방식으로 문제를 해결하기 위해 정의됩니다 (예외가 있음).


-1

그에 대한 또 다른 요소는 사용 가능한 연산자로 작업을 정의하는 것이 항상 쉬운 것은 아니라는 것입니다. 예, 어떤 종류의 숫자에 대해서도 '*'연산자는 의미가 있으며 일반적으로 언어 또는 기존 모듈로 구현됩니다. 그러나 일반적인 복잡한 클래스 (shipingAddress, WindowManager, ObjectDimensions, PlayerCharacter 등)를 정의 해야하는 복잡한 동작의 경우 동작이 명확하지 않습니다 ... 주소에 숫자를 더하거나 빼는 것은 무엇을 의미합니까? 두 개의 주소를 곱 하시겠습니까?

물론, ShippingAddress 클래스에 문자열을 추가하는 것은 "setLine1"함수 대신 "주소에서 라인 1 바꾸기"와 같은 사용자 정의 조작을 의미하고 "setZipCode"대신 숫자를 추가하는 것은 "우편 번호 교체"입니다. 그러나 코드는 읽기 쉽고 혼란스럽지 않습니다. 우리는 일반적으로 연산자가 기본 유형 / 클래스에 사용된다고 생각합니다. 그들의 행동은 직관적이고 명확하며 일관성이 있습니다 (적어도 언어에 익숙한 경우). Integer, String, ComplexNumbers 등과 같은 유형을 생각하십시오.

따라서 특정 경우에 연산자를 정의하는 것이 매우 유용 할 수 있더라도 실제 언어 구현에서는 99 %가 이미 기본 언어 패키지에 구현되어 있기 때문에 실제 구현은 상당히 제한적입니다.

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