왜 그렇게 많은 언어가 가치를 지니고 있습니까?


36

당신이 항상 값에 의해 전달 된 것 C와 같은 명시적인 포인터 조작이 심지어 언어 (당신은 할 수 있습니다 참조로 전달할하지만 기본 동작이 아니다).

이것의 장점은 무엇입니까, 왜 그렇게 많은 언어가 값으로 전달되고 다른 언어 가 참조로 전달 됩니까? (확실하지는 않지만 Haskell이 참조로 전달된다는 것을 이해합니다).


5
void acceptEntireProgrammingLanguageByValue(C++);
Thomas Eding 2016 년

4
더 나빠질 수 있습니다. 일부 오래된 언어들도 이름으로 전화를
hugomg

25
실제로 C에서는 참조로 전달할 수 없습니다. value로 포인터를 전달할 수 있습니다 . 이는 참조 별 전달과 매우 유사하지만 같은 것은 아닙니다. 그러나 C ++에서는 참조로 전달할 수 있습니다.
메이슨 휠러

@Mason Wheeler 좀 더 정교하게 만들거나 링크를 추가 할 수 있습니까? C / C ++ 전문가가 아니기 때문에 평범한 프로그래머이므로 감사의 말이 저에게 명확하지 않습니다.
Betlista

1
@Betlista : 참조로 전달하면 다음과 같은 스왑 루틴을 작성할 수 있습니다. temp := a; a := b; b := temp;반환되면 a및 의 값 b이 바뀝니다. C에서는 그렇게 할 수 없습니다. 당신은 포인터 통과해야 a하고을 b하고 swap루틴은 그들이 가리키는 값에 따라 행동해야한다.
메이슨 휠러

답변:


58

값으로 전달하는 것은 실수로 매개 변수를 분석법 / 기능으로 수정할 수 없기 때문에 참조로 전달하는 것보다 안전합니다. 함수에 제공하는 변수에 대해 걱정할 필요가 없기 때문에 언어를보다 간단하게 사용할 수 있습니다. 당신은 그들이 변경되지 않을 것을 알고 있으며, 이것은 종종 당신이 기대하는 것 입니다.

이 경우, 원하는 매개 변수를 수정하려면이 명확 (포인터를 전달)을 만드는 몇 가지 명시 적 작업이 필요합니다. 이렇게하면 모든 호출자가 약간 다르게 호출하도록하고 ( &variableC에서) 변수 매개 변수가 변경 될 수 있음을 명시 적으로 만듭니다.

따라서 변수에 명시 적으로 표시되어 있지 않은 한 함수가 변수 매개 변수를 변경하지 않는다고 가정 할 수 있습니다 (포인터를 전달해야 함). 이것은 대안보다 안전하고 깨끗한 솔루션입니다. 구체적으로 할 수없는 경우가 아니라면 모든 것이 매개 변수를 변경할 수 있다고 가정하십시오.


4
+1 포인터로 붕괴되는 배열은 주목할만한 예외를 나타내지 만 전반적인 설명은 좋습니다.
dasblinkenlight 2016 년

6
@dasblinkenlight : 배열은 C에서 아픈 지점입니다 :(
Matthieu M.

55

값별 호출 및 참조 별 호출은 오래 전에 매개 변수 전달 모드로 오인 된 구현 기술입니다.

처음에는 FORTRAN이있었습니다. 서브 루틴이 매개 변수를 수정할 수 있어야하고 FORTRAN이 처음 정의 될 때 프로그래밍에 대해 충분히 알려지지 않았기 때문에 서브 루틴이 매개 변수를 수정할 수 있어야하기 때문에 FORTRAN은 참조 별 호출 만있었습니다.

ALGOL은 이름 별 호출 및 값별 호출을 제안했습니다. 값별 호출은 변경되어서는 안되는 것들 (입력 매개 변수)을위한 것입니다. 이름 별 호출은 출력 매개 변수를위한 것입니다. Call-by-name은 주요 그릇으로 밝혀졌고 ALGOL 68은 그것을 떨어 뜨 렸습니다.

PASCAL은 가치 별 통화 및 기준 별 통화를 제공했습니다. 프로그래머가 매개 변수 스택을 날리지 않도록 큰 객체 (일반적으로 배열)를 참조로 전달한다고 컴파일러에게 알려주는 방법을 제공하지는 않았지만 객체를 변경해서는 안됩니다.

PASCAL은 언어 디자인 어휘에 대한 포인터를 추가했습니다.

C는 메모리에있는 임의의 개체에 대한 포인터를 반환하도록 kludge 연산자를 정의하여 값별 호출을 제공하고 참조 별 시뮬레이션 호출을 제공했습니다.

이후의 언어는 대부분 디자이너가 다른 것을 본 적이 없기 때문에 C를 복사했습니다. 이것이 바로 가치 별 통화가 인기있는 이유 일 것입니다.

C ++은 C kludge 위에 kludge를 추가하여 참조 별 호출을 제공합니다.

이제 값별 호출 대 기준 별 호출 대 포인터 별 kludge의 직접적인 결과로 C와 C ++ (프로그래머)는 const 포인터와 const에 대한 포인터 (읽기 전용)가있는 끔찍한 두통이 있습니다. 사물.

에이다는이 악몽을 피할 수있었습니다.

Ada에는 명시적인 값별 호출 대 참조 별 호출이 없습니다. 오히려 Ada는 매개 변수 (읽지 만 쓸 수는 없음), 매개 변수 (읽기 전에 반드시 기록해야 함) 및 어떤 순서로든 읽고 쓸 수있는 매개 변수를 가지고 있습니다. 컴파일러는 특정 매개 변수가 값 또는 참조로 전달되는지 여부를 결정합니다. 프로그래머에게 투명합니다.


1
+1. 이것은 지금까지 본 것 중 실제로 유일무이 한 질문에 대한 답이며, 이해하기 쉬운 질문이며 프로 -FP 요청에 대한 투명한 변명으로 사용하지 않습니다.
메이슨 휠러

11
"일부 언어는 C를 복사했습니다. 대부분 디자이너가 다른 것을 본 적이 없기 때문입니다." 0이 붙은 8 진 상수를 가진 새로운 언어를 볼 때마다 조금씩 죽습니다.
librik

4
이것은 프로그래밍 성경의 첫 번째 문장이 될 수 있습니다 In the beginning, there was FORTRAN.

1
ADA와 관련하여 전역 변수가 입력 / 출력 매개 변수에 전달되면 중첩 된 호출에서 변수를 검사하거나 수정하지 못하게됩니까? 그렇지 않다면 입출력 매개 변수와 참조 매개 변수 사이에 명확한 차이가 있다고 생각합니다. 개인적으로, 언어가 "in / out / in + out"과 "value / ref / don't-care"의 모든 조합을 명시 적으로 지원하기를 원하므로 프로그래머는 의도에 대해 최대한의 명확성을 제공 할 수 있지만 최적화 프로그램은 [프로그래머 의도에 지장을주지 않는 한] 구현에서 최대의 유연성을 갖습니다.
supercat 2016 년

2
@Neikos 0접두사가 기본이 아닌 10 개의 접두사와 일치하지 않는다는 사실도 있습니다 . 진수가 있다면 즉, C에서 다소 용서할 (주로 소스 호환 언어, 예를 들어 C ++, 오브젝티브 C)있을 수 있습니다 더 현대적인 언어를 모두 사용하는 경우 도입 할 때 다른 기지 만, 0x그리고 0처음부터 그냥하게 그들을 제대로 볼 신중하게 생각하다.
8bittree

13

참조에 의한 통과는 의도하지 않은 동작을 유발할 때 추적하기가 불가능하기 때문에 매우 미묘한 의도하지 않은 부작용을 허용합니다.

특히, 값에 의해 전달 final, static또는 const입력 매개 변수는 버그가 사라의이 전체 클래스를 만든다.

불변 언어는 더 결정 론적이며, 무슨 일이 일어나고 있는지, 어떤 기능에서 나오는지에 대해 추론하고 이해하기가 더 쉽습니다.


7
그것은 모든 것이 기본적으로 참조되는 '고대'VB 코드를 디버깅하려고 시도한 후에 알아내는 것 중 하나입니다.
jfrankcarr 2016 년

어쩌면 그것은 내 배경이 다르다는 것이지만, 나는 당신이 말하는 "추적하기 어려운 다음에는 매우 미묘한 의도하지 않은 부작용"이 무엇인지 전혀 모른다. 나는 값에 의한 기본값이 파스칼에 익숙하지만 매개 변수 (기본적으로 C #과 동일한 모델)를 명시 적으로 표시하여 참조 기준을 사용할 수 있으며 문제가 발생하지 않았습니다. By-ref가 기본값 인 "Ancient VB"에서 문제가되는 방법을 알 수 있지만 By-ref가 옵트 인 인 경우 작성하는 동안 생각할 수 있습니다.
메이슨 휠러

4
@MasonWheeler 뒤에서 오는 초보자는 어떻습니까? 이해하지 못한 채 복사 한 후 간접적으로 그 상태를 조작하기 시작하면 실제 세계는 항상 발생합니다. 덜 간접적 인 것이 좋습니다. 불변이 최고입니다.

1
@MasonWheeler Swap임시 변수를 사용하지 않는 해킹 과 같은 간단한 별칭 예제 는 문제 자체는 아니지만 더 복잡한 예제를 디버깅하는 것이 얼마나 어려운지를 보여줍니다.
Mark Hurd

1
@MasonWheeler : FORTRAN의 전형적인 예는 "변수는 그렇지 않습니다; 상수는 그렇지 않습니다"라는 말로 이어졌고 3.0과 같은 부동 소수점 상수를 전달 된 매개 변수를 수정하는 함수에 전달합니다. 함수에 전달 된 부동 소수점 상수의 경우 시스템은 적절한 값으로 초기화되고 함수에 전달 될 수있는 "숨겨진"변수를 작성합니다. 함수가 1.0을 매개 변수에 추가하면 프로그램에서 3.0의 임의 하위 집합이 갑자기 4.0이 될 수 있습니다.
supercat

8

왜 그렇게 많은 언어가 가치를 지니고 있습니까?

큰 프로그램을 작은 서브 루틴으로 나누는 요점은 서브 루틴에 대해 독립적으로 추론 할 수 있다는 것입니다. 패스 바이 레퍼런스는이 속성을 손상시킵니다. (공유 변경 가능 상태와 마찬가지로)

당신이 항상 값에 의해 전달 된 것 C와 같은 명시적인 포인터 조작이 심지어 언어 (당신은 할 수 있습니다 참조로 전달할하지만 기본 동작이 아니다).

실제로 C는 항상 값으로 전달되며 참조로 전달되지 않습니다. 당신은 어떤 주소를 가지고 그 주소를 전달할 수 있지만, 그 주소는 여전히 값으로 전달 될 것입니다.

이것의 장점은 무엇입니까, 왜 그렇게 많은 언어가 값으로 전달되고 다른 언어 가 참조로 전달 됩니까?

통과 기준을 사용하는 데는 두 가지 주요 이유가 있습니다.

  1. 여러 반환 값 시뮬레이션
  2. 능률

개인적으로 # 1은 가짜라고 생각합니다. 거의 항상 잘못된 API 및 / 또는 언어 디자인에 대한 변명입니다.

  1. 여러 개의 반환 값이 필요한 경우 시뮬레이션하지 말고 지원하는 언어 만 사용하십시오.
  2. 여러 반환 값을 튜플과 같은 가벼운 데이터 구조로 패키징하여 여러 반환 값을 시뮬레이션 할 수도 있습니다. 언어가 패턴 일치 또는 파괴 바인딩을 지원하는 경우 특히 효과적입니다. 예 : 루비 :

    def foo
      # This is actually just a single return value, an array: [1, 2, 3]
      return 1, 2, 3
    end
    
    # Ruby supports destructuring bind for arrays: a, b, c = [1, 2, 3]
    one, two, three = foo
    
  3. 종종 여러 개의 반환 값이 필요하지 않습니다. 예를 들어, 인기있는 패턴 중 하나는 서브 루틴이 오류 코드를 리턴하고 실제 결과는 참조로 다시 쓰여지는 것입니다. 대신 오류가 예상치 않은 경우 예외를 발생 시키거나 Either<Exception, T>오류가 예상 되는 경우를 반환 해야합니다. 다른 패턴은 작업이 성공했는지 여부를 알려주고 실제 결과를 참조로 리턴하는 부울을 리턴하는 것입니다. 다시 말하지만 실패가 예상치 못한 경우 대신 예외를 처리해야합니다. 예를 들어 사전에서 값을 찾을 때 실패가 예상되는 경우 대신 예외를 리턴해야 Maybe<T>합니다.

값을 복사 할 필요가 없으므로 참조 별 전달이 값별 전달보다 더 효율적일 수 있습니다.

(확실하지는 않지만 Haskell이 참조로 전달된다는 것을 이해합니다).

아니요, Haskell은 참조로 전달되지 않습니다. 또한 가치를 전달하는 것도 아닙니다. 기준 별 통과 및 값별 통과는 모두 엄격한 평가 전략이지만 Haskell은 엄격하지 않습니다.

사실, 하스켈 사양은 지정하지 않는 어떤 특정 평가 전략을. 대부분의 Hakell 구현은 이름 별 호출과 필요에 따른 호출 (메모와 함께 호출 별 변형)을 사용하지만 표준에서는이를 요구하지 않습니다.

엄격한 언어의 경우에도 참조 언어를 변경하는 경우에만 기능 언어 간의 차이를 관찰 할 수 있으므로 기능 언어의 참조 별 전달 또는 값별 전달을 구분하는 것은 의미가 없습니다. 따라서 구현자는 언어의 의미를 어지럽히 지 않고 둘 사이에서 자유롭게 선택할 수 있습니다.


9
"여러 개의 반환 값이 필요한 경우 시뮬레이션하지 말고 지원하는 언어 만 사용하십시오." 이것은 조금 이상한 이야기입니다. 기본적으로 언어는 당신이 필요로하는 모든 것을 할 수있는 경우 "라고,하지만 하나 개의 기능을 사용하면 아마 코드의 1 % 미만에 필요한거야 -하지만 여전히 그 1 %를 위해 필요 -에서 수행 할 수 없습니다 특히 깨끗한 방법이라면, 당신의 언어는 프로젝트에 충분하지 않으며 모든 것을 다른 언어로 다시 작성해야합니다. " 미안하지만 그건 우스운 일입니다.
메이슨 휠러

+1이 점에 전적으로 동의 합니다 큰 프로그램을 작은 서브 루틴으로 나누는 요점은 서브 루틴을 독립적으로 추론 할 수 있다는 것입니다. 패스 바이 레퍼런스는이 속성을 손상시킵니다. (공유 변경 가능 상태와 마찬가지로)
Rémi

3

언어의 호출 모델, 인수 유형 및 언어의 메모리 모델에 따라 다른 동작이 있습니다.

단순 기본 유형의 경우 값을 전달하면 레지스터에서 값을 전달할 수 있습니다. 메모리에서 값을로드하거나 다시 저장할 필요가 없으므로 매우 빠릅니다. 호출자가 오브젝트의 호출자 사본을 망칠 위험없이 인수가 완료되면 인수가 사용한 메모리를 재사용함으로써 유사한 최적화가 가능합니다. 인수가 임시 객체라면 아마도 전체 사본을 저장했을 것입니다 (C ++ 11은 새로운 오른쪽 참조 및 이동 의미론을 통해 이러한 종류의 최적화를보다 분명하게 만듭니다).

많은 OO 언어에서 (이 컨텍스트에서는 C ++이 더 예외 임) 개체를 값으로 전달할 수 없습니다. 참조로 전달해야합니다. 이것은 기본적으로 코드를 다형성으로 만들고 OO에 적합한 인스턴스 개념과 더 일치합니다. 또한 값을 기준으로 전달하려면 이러한 작업을 생성 한 성능 비용을 확인하여 직접 사본을 만들어야합니다. 이 경우 언어는 최상의 성능을 제공 할 수있는 접근 방식을 선택합니다.

기능적 언어의 경우, 값이나 참조를 전달하는 것이 단순히 최적화의 문제라고 생각합니다. 이러한 언어의 기능은 순수하고 부작용이 없으므로 속도를 제외하고 값을 복사 할 이유가 없습니다. 나는 그러한 언어가 종종 같은 값을 가진 동일한 객체의 사본을 공유한다는 것을 확신합니다. 이는 당신이 (const) 참조 의미론을 사용하는 경우에만 사용할 수 있습니다. 파이썬은 또한 정수와 문자열이 정수와 문자열이 파이썬에서 상수 객체 인 이유를 설명하는 정수 및 일반 문자열에이 트릭을 사용했습니다. 이는 또한 컨텐츠 비교 대신 포인터 비교를 허용하고 일부 내부 데이터의 지연 평가를 수행하여 코드를 다시 최적화하는 데 도움이됩니다.


2

값으로 표현을 전달할 수 있습니다. 자연 스럽습니다. (임시) 참조로 표현을 전달하는 것은 ... 이상합니다.


1
또한 임시 참조로 표현을 전달하면 버그가 생길 수 있습니다 (상태가 좋은 언어). 단순히 변수를 변경하면 (단지 일시적이므로) 실제로 변수를 전달하면 역효과가 발생하므로 foo 전달과 같은 추악한 해결 방법을 고안해야합니다 foo 대신 +0.
herby 2016 년

2

참조로 전달하면 전역의 모든 문제 (즉, 범위 및 의도하지 않은 부작용)와 함께 항상 전역 값으로 효과적으로 작업하고 있습니다.

세계와 마찬가지로 참고 문헌은 유익하지만 때로는 첫 번째 선택이되어서는 안됩니다.


3
나는 첫 번째 문장에서 참조로 통과를 의미한다고 가정합니까?
jk.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.