많은 언어가 명명 된 매개 변수를 지원하지 않는 이유는 무엇입니까? [닫은]


14

함수를 호출 할 때 다음과 같이 작성할 수 있다면 코드를 읽는 것이 얼마나 쉬운 지 생각하고있었습니다.

doFunction(param1=something, param2=somethingElse);

단점을 생각할 수 없으며 코드를 훨씬 더 읽기 쉽게 만듭니다. 배열을 유일한 인수로 전달하고 배열 키를 매개 변수 이름으로 가질 수 있지만 여전히 읽을 수는 없습니다.

내가 놓친 단점이 있습니까? 그렇지 않다면 왜 많은 언어가 이것을 허용하지 않습니까?


1
명명 된 매개 변수가 있어야하지만 가질 수없는 언어를 제공 할 수 있습니까?
브라이언 첸

1
많은 경우에 너무 장황하다고 생각합니다. 내가 본 한, 언어가 이것을 사용하는지 아닌지는 언어가 긍정적 또는 부정적으로 언어에 영향을 미치는지 여부에 달려 있습니다. 대부분의 C 스타일 언어는 그것없이 잘 작동합니다. 그들의 경우에, 어쨌든 무슨 일이 벌어지고 있는지가 종종 명백하며, 그것의 부재는 실제로 일반적인 혼란을 줄이는 데 도움이됩니다.
Panzercrisis

2
@SteveFallows : "Named Parameter Idiom"은 빌더 클래스에서 유창한 API (Martin Fowler에 의해 명명 된)를 정의하는 이상한 이름처럼 보입니다. Joshua Bloch의 Effective Java 의 첫 번째 항목 중 하나에서 동일한 패턴의 예를 찾을 수 있습니다 .
jhominal

3
이것은 반드시 의견에 근거한 질문은 아닙니다
Catskul

2
동의했다. "왜 많은 언어가 명명 된 매개 변수를 지원하지 않습니까?" 의견보다는 언어의 역사에 대한 질문입니다. 누군가가 "파라미터 이름을 더 잘 지정하지 않았습니까?"라고 물으면 의견이 될 것입니다.
Andy Fleming

답변:


14

매개 변수 이름을 지정해도 코드를 항상 더 읽기 쉽게 만들 수 는 없습니다 . 예를 들어, min(first=0, second=1)또는 min(0, 1)?

이전 단락의 인수를 받아들이면 매개 변수 이름을 반드시 지정해야하는 것은 아닙니다. 모든 언어가 매개 변수 이름을 유효한 옵션으로 지정하지 않는 이유는 무엇입니까?

적어도 두 가지 이유를 생각할 수 있습니다.

  • 일반적으로 이미 충족 된 요구 (여기서는 매개 변수 전달)를위한 두 번째 구문을 도입하는 것은 도입 된 언어 복잡성의 고유 한 비용 (언어 구현 및 언어의 프로그래머의 마음 모델에서)과 균형이 맞아야합니다. ;
  • 매개 변수 이름 변경은 API 주요 변경 사항으로, 매개 변수 이름 변경은 사소한 비파괴 적 변경이라는 개발자의 기대에 맞지 않을 수 있습니다.

선택적 매개 변수 (명명 된 매개 변수가 필요함)를 구현하지 않고 명명 된 매개 변수를 구현하는 언어는 알지 못하므로 가독성 이점을 과대 평가하는 것에주의해야합니다. Fluent Interfaces .


6
나는 정기적으로 여러 다른 언어로 코딩하고 거의 항상 주어진 언어의 간단한 하위 문자열에 대한 매개 변수를 찾아야합니다. 하위 문자열 (시작, 길이) 또는 하위 문자열 (시작, 종료)이며 종료가 문자열에 포함됩니까?
제임스 앤더슨

1
@JamesAnderson-명명 된 매개 변수 하위 문자열로 문제를 어떻게 해결할 수 있습니까? 두 번째 매개 변수의 이름이 무엇인지 알기 위해 여전히 매개 변수를 찾아야합니다 (함수가 존재하고 언어의 다형성 기능이 다른 유형의 동일한 유형 매개 변수를 허용하지 않는 한).
mouviciel

6
@mouviciel : 명명 된 매개 변수는 작가 에게는 전혀 사용되지 않지만 독자 에게는 환상적입니다 . 우리는 모두 변수 이름이 읽을 수있는 코드를 만드는 데 얼마나 중요한지 알고 있으며, 명명 된 매개 변수를 사용하면 인터페이스를 만든 사람이 좋은 매개 변수 이름과 함수 이름을 지정할 수 있으므로 해당 인터페이스를 사용하는 사람들이 쉽게 읽을 수있는 코드를 작성할 수 있습니다.
Phoshi

@Phoshi-맞습니다. 이전 의견을 쓸 때 코드를 읽는 것이 중요하다는 것을 잊었습니다.
mouviciel

14

명명 된 매개 변수를 사용하면 코드를 읽기 쉽고 쓰기 어렵게 만듭니다.

코드를 읽을 때 명명 된 매개 변수는 코드를 이해하기 쉽게 만드는 컨텍스트를 도입 할 수 있습니다. 예를 들어이 생성자를 고려하십시오 Color(1, 102, 205, 170). 지구상에서 무슨 의미입니까? 실제로, Color(alpha: 1, red: 102, green: 205, blue: 170)읽기가 훨씬 쉬울 것입니다. 그러나 아아, 컴파일러는 "아니오"를 말한다 - 그것은 원한다 Color(a: 1, r: 102, g: 205, b: 170). 명명 된 매개 변수를 사용하여 코드를 작성할 때 정확한 이름을 찾는 데 불필요한 시간을 소비합니다. 순서를 잊어 버리는 것보다 일부 매개 변수의 정확한 이름을 잊는 것이 더 쉽습니다.

DateTime거의 동일한 인터페이스로 포인트와 지속 시간에 두 개의 형제 클래스가 있는 API를 사용할 때 한 번 물었습니다 . DateTime->new(...)허용되는 동안second => 30 인수의 DateTime::Duration->new(...)seconds => 30및 기타 장치에 대한 유사. 그렇습니다, 그것은 절대적으로 의미가 있지만, 이것은 명명 된 매개 변수 ≠ 사용의 용이함을 보여주었습니다.

나쁜 이름도 읽기가 쉽지 않습니다

명명 된 매개 변수가 나쁜 방법의 또 다른 예는 아마도 R 언어 일 것입니다 . 이 코드는 데이터 플롯을 만듭니다.

plot(plotdata$n, plotdata$mu, type="p", pch=17,  lty=1, bty="n", ann=FALSE, axes=FALSE)

xy 데이터 행에 대한 두 개의 위치 인수 와 이름 지정된 매개 변수 목록이 표시됩니다. 기본값에는 더 많은 옵션이 있으며 기본값을 변경하거나 명시 적으로 지정하려는 옵션 만 나열됩니다. 이 코드가 매직 넘버를 사용한다는 것을 무시하고 열거 형 (R이 있다면!)을 사용하면 이점을 얻을 수 있습니다.

  • pch실제로는 모든 데이터 포인트에 대해 그려지는 그림 문자 인 그림 문자입니다. 17빈 원 또는 이와 비슷한 것입니다.
  • lty선 종류입니다. 여기에 1실선이다.
  • bty박스 타입입니다. "n"플롯 주위에 상자가 그려지지 않도록 설정합니다 .
  • ann 축 주석의 모양을 제어합니다.

각 약어의 의미를 모르는 사람에게는 이러한 옵션이 다소 혼동됩니다. 또한 R이 이러한 레이블을 사용하는 이유를 밝힙니다. 자체 문서화 코드가 아니라 값을 올바른 변수에 매핑하기위한 키로 (동적 형식 언어).

매개 변수 및 서명의 속성

함수 서명에는 다음과 같은 속성이있을 수 있습니다.

  • 인수는 순서가 있거나 순서가 없을 수 있습니다.
  • 명명되거나 명명되지 않은,
  • 필수 또는 선택.
  • 서명은 크기 나 유형별로 오버로드 될 수 있습니다.
  • varargs로 지정되지 않은 크기를 가질 수 있습니다.

다른 언어는이 시스템의 다른 좌표에 위치합니다. C에서 인수는 순서가 지정되고 이름이 지정되지 않으며 항상 필요하며 varargs 일 수 있습니다. Java에서는 서명이 오버로드 될 수 있다는 점을 제외하면 상황이 유사합니다. Objective C에서 서명은 주문, 이름 지정, 필수이며 C 주변의 구문 설탕이므로 오버로드 할 수 없습니다.

varargs (명령 줄 인터페이스, Perl,…)가있는 동적 유형 언어는 선택적 명명 된 매개 변수를 에뮬레이션 할 수 있습니다. 서명 크기가 오버로드 된 언어에는 위치 선택적 매개 변수와 같은 것이 있습니다.

명명 된 매개 변수를 구현하지 않는 방법

명명 된 매개 변수를 생각할 때 일반적으로 명명 된 선택적, 정렬되지 않은 매개 변수를 가정합니다. 이를 구현하는 것은 어렵다.

선택적 매개 변수는 기본값을 가질 수 있습니다. 이들은 호출 된 함수에 의해 지정되어야하며 호출 코드로 컴파일되어서는 안됩니다. 그렇지 않으면 모든 종속 코드를 다시 컴파일하지 않고 기본값을 업데이트 할 수 없습니다.

이제 중요한 질문은 인수가 실제로 함수에 전달되는 방법입니다. 정렬 된 매개 변수를 사용하면 arg를 레지스터 또는 스택에서 고유 한 순서로 전달할 수 있습니다. 레지스터를 잠시 제외시킬 때 문제는 정렬되지 않은 선택적 인수를 스택에 넣는 방법입니다.

이를 위해서는 선택적 인수보다 순서가 필요합니다. 선언 코드가 변경되면 어떻게됩니까? 순서는 관련이 없으므로 함수 선언에서 재정렬하면 스택에서 값의 위치가 변경되지 않아야합니다. 새로운 선택적 매개 변수를 추가 할 수 있는지 고려해야합니다. 이전에는 해당 매개 변수를 사용하지 않은 코드가 여전히 새 매개 변수와 함께 작동해야하므로 사용자 관점에서 이것은 그렇게 보입니다. 따라서 선언에서 주문을 사용하거나 알파벳 순서를 사용하는 것과 같은 주문은 제외됩니다.

서브 타이핑과 Liskov 대체 원칙에 비추어 이것을 고려하십시오 – 컴파일 된 출력에서, 동일한 명령어는 아마도 새로운 명명 된 매개 변수를 가진 서브 타입과 수퍼 타입에서 메소드를 호출 할 수 있어야합니다.

가능한 구현

결정적인 순서를 가질 수 없다면 순서없는 데이터 구조가 필요합니다.

  • 가장 간단한 구현은 매개 변수 이름과 값을 전달하는 것입니다. Perl에서 또는 명령 행 도구를 사용하여 명명 된 매개 변수를 에뮬레이트하는 방법입니다. 이렇게하면 위에서 언급 한 모든 확장 문제를 해결할 수 있지만 성능에 중요한 코드의 옵션이 아닌 공간 낭비가 클 수 있습니다. 또한 이러한 명명 된 매개 변수를 처리하는 것은 단순히 스택에서 값을 팝하는 것보다 훨씬 복잡합니다.

    실제로 문자열 풀링을 사용하여 공간 요구 사항을 줄일 수 있습니다.이 경우 문자열 비교는 포인터 비교에 대한 이후의 문자열 비교를 줄일 수 있습니다 (정적 문자열이 실제로 풀링되는 것을 보장 할 수없는 경우에는 두 문자열을 비교해야합니다). 세부 묘사).

  • 대신 명명 된 인수의 사전으로 작동하는 영리한 데이터 구조를 전달할 수도 있습니다. 키 세트가 정적으로 호출 위치에 알려지기 때문에 호출자 측에서 저렴합니다. 이를 통해 완벽한 해시 함수를 만들거나 트라이를 미리 계산할 수 있습니다. 수신자는 여전히 다소 비싼 모든 가능한 매개 변수 이름이 있는지 테스트해야합니다. 이와 같은 것이 Python에서 사용됩니다.

대부분의 경우 너무 비쌉니다.

명명 된 매개 변수가있는 함수를 올바르게 확장 할 수있는 경우 명확한 순서를 가정 할 수 없습니다. 따라서 두 가지 솔루션 만 있습니다.

  • 명명 된 매개 변수의 순서를 서명의 일부로 만들고 나중에 변경을 허용하지 마십시오. 이것은 자체 문서화 코드에는 유용하지만 선택적 인수에는 도움이되지 않습니다.
  • 키-값 데이터 구조를 수신자에게 전달하면 유용한 정보를 추출해야합니다. 이것은 비교가 매우 비싸고, 성능에 중점을 두지 않고 스크립팅 언어에서만 볼 수 있습니다.

다른 함정

함수 선언의 변수 이름은 일반적으로 내부적으로 의미가 있으며 많은 문서 도구가 여전히 표시하더라도 인터페이스의 일부가 아닙니다. 많은 경우 내부 변수와 해당 명명 된 인수에 다른 이름을 원할 것입니다. 명명 된 매개 변수의 외부에서 볼 수있는 이름을 선택할 수없는 언어는 변수 이름이 호출 컨텍스트를 염두에두고 사용되지 않으면 많은 것을 얻지 못합니다.

명명 된 인수의 에뮬레이션 문제는 호출자 측에서 정적 검사가 부족하다는 것입니다. 인수 사전을 전달할 때 (Python을 보면) 잊어 버리기가 특히 쉽습니다. 사전을 전달하는 것이 일반적인 해결 방법 (예 : JavaScript)이기 때문에 중요합니다.foo({bar: "baz", qux: 42}) 합니다. 여기서는 값의 유형이나 특정 이름의 존재 또는 부재를 정적으로 확인할 수 없습니다.

명명 된 매개 변수 에뮬레이션 (정적으로 형식화 된 언어)

문자열을 키로 사용하고 객체를 값으로 사용하는 것은 정적 유형 검사기에는 유용하지 않습니다. 그러나 명명 된 인수는 구조체 또는 객체 리터럴로 에뮬레이션 할 수 있습니다.

// Java

static abstract class Arguments {
  public String bar = "default";
  public int    qux = 0;
}

void foo(Arguments args) {
  ...
}

/* using an initializer block */
foo(new Arguments(){{ bar = "baz"; qux = 42; }});

3
" it is easier to forget the exact names of some parameters than it is to forget their order"... 흥미로운 주장입니다.
Catskul

1
첫 번째 반례는 명명 된 매개 변수의 문제가 아니며 영어 복수형이 인터페이스의 필드 이름에 매핑되는 방식에 결함이 있습니다. 두 번째 반례는 비슷하게 이름을 잘못 지정하는 개발자의 문제입니다. (매개 변수 전달 인터페이스의 선택은 그 자체 만으로는 가독성을위한 충분한 조건 이 될 수 없습니다. 문제는 그것이 필요한 조건 인지 아닌지에 대한 도움입니다).
mtraceur

1
구현 비용 및 명명 된 매개 변수 수행과의 상충 관계에 대한 전체 지점의 경우 +1입니다. 나는 단지 시작이 사람들을 잃을 것이라고 생각합니다.
mtraceur

@mtraceur 포인트가 있습니다. 5 년 후, 아마도 답을 완전히 다르게 쓸 것입니다. 원한다면 내 대답을 편집하여 덜 유용한 것들을 제거 할 수 있습니다. (보통 그러한 편집은 저자의 의도와 모순되므로 거부 될 것이지만 여기서는 괜찮습니다). 예를 들어 이제는 많은 명명 된 매개 변수 문제가 동적 언어의 제한 일 뿐이며 형식 시스템 만 가져야한다고 생각합니다. 예를 들어 JavaScript에는 Flow 및 TypeScript가 있고 Python에는 MyPy가 있습니다. 대부분의 사용성 문제를 제거하는 적절한 IDE 통합 나는 아직도 Perl에서 무언가를 기다리고있다.
amon

7

헝가리 표기법이 더 이상 널리 사용되지 않는 것과 같은 이유로; Intellisense (또는 Microsoft 이외의 IDE에서는 그와 동등한 수준). 대부분의 최신 IDE는 단순히 매개 변수 참조 위에 마우스를 올려 놓아 매개 변수에 대해 알아야 할 모든 것을 알려줍니다.

즉, C # 및 Delphi를 비롯한 여러 언어가 명명 된 매개 변수를 지원합니다. C #에서는 선택 사항이므로 원하지 않는 경우에는 사용할 필요가 없으며 개체 초기화와 같이 멤버를 구체적으로 선언하는 다른 방법이 있습니다.

명명 된 매개 변수는 대부분 선택적 매개 변수의 하위 집합 만 지정하려는 경우에 유용합니다. C #에서는 프로그래머에게 이러한 유연성을 제공하기 위해 오버로드 된 메서드를 더 이상 필요로하지 않기 때문에 매우 유용합니다.


4
파이썬은 명명 된 매개 변수를 사용하며 언어의 훌륭한 기능입니다. 더 많은 언어가 사용되기를 바랍니다. C ++에서와 같이 더 이상 기본값을 "전에"인수를 명시 적으로 지정하지 않아도 기본 인수가 더 쉬워집니다. (마지막 단락에서와 마찬가지로, 파이썬은 오버로드가 없어서 어떻게 파이썬이 도망 치는지입니다 ... 기본 인수와 이름 인수는 모두 동일한 작업을 수행 할 수 있습니다.) 명명 된 인수는 더 읽기 쉬운 코드를 만듭니다. 와 같은 sin(radians=2)것이 있으면 "Intellisense"가 필요하지 않습니다.
로봇 고트

2

Bash는 확실히이 스타일의 프로그래밍을 지원하고 Perl과 Ruby는 이름 / 값 매개 변수 목록 전달을 지원합니다 (기본 해시 / 맵 지원 언어와 동일). 매개 변수에 이름을 첨부하는 구조체 / 레코드 또는 해시 / 맵을 정의하도록 선택하는 것을 막을 수있는 것은 없습니다.

기본적으로 해시 / 맵 / 키값 저장소를 포함하는 언어의 경우, 키 / 값 해시를 함수 / 메서드에 전달하기 위해 관용구를 채택하여 원하는 기능을 얻을 수 있습니다. 프로젝트에서 사용해 본 후에는 사용 편의성으로 인한 생산성 향상 또는 결함 감소를 통해 품질 향상을 통해 무엇을 얻었는지 더 잘 파악할 수 있습니다.

숙련 된 프로그래머는 주문 / 슬롯별로 매개 변수를 전달하는 데 익숙해졌습니다. 또한이 관용구가 몇 개 이상의 주장 (예 :> 5/6)이있는 경우에만 가치가 있음을 알 수 있습니다. 그리고 많은 수의 주장이 복잡한 방법을 나타내는 경우가 많기 때문에이 관용구는 가장 복잡한 방법에만 유익하다는 것을 알 수 있습니다.


2

나는 그것이 CB가 VB.net보다 인기있는 이유와 같은 이유라고 생각합니다. VB.NET은보다 "읽을 수있는"반면, 닫는 대괄호 대신 "end if"를 입력하면 코드를 더 많이 채우고 이해하기가 더 어려워집니다.

코드를 이해하기 쉽게 만드는 것은 간결함입니다. 적은 것이 좋습니다. 함수 매개 변수 이름은 일반적으로 어쨌든 분명하며 실제로 코드를 이해하는 데 도움이되지는 않습니다.


1
나는 "낮을수록 좋다"는 것에 동의하지 않는다. 논리적으로 극단적 인 코드 골프 우승자는 "최고의"프로그램이 될 것입니다.
라일리 메이저

2

명명 된 매개 변수는 소스 코드 를 리팩토링 할 때 발생하는 문제에 대한 해결책 이며 소스 코드를 더 읽기 쉽게 만들지 않습니다 . 명명 된 매개 변수는 컴파일러 / 파서가 함수 호출의 기본값을 확인하는 데 도움이됩니다. 의미있는 주석을 추가하는 것보다 코드를 더 읽기 쉽게 만들려면

리팩토링하기 어려운 언어는 종종 변경 foo(1)시그니처가 깨질 것이기 때문에 명명 된 매개 변수를 지원 foo()하지만 foo(name:1), 프로그래머가 변경하기 위해 노력을 덜 요구할 가능성이 적습니다 foo.

다음 함수에 새 매개 변수를 도입해야 할 때 어떻게해야합니까? 그 함수를 호출하는 수백 또는 수천 줄의 코드가 있습니까?

foo(int weight, int height, int age = 0);

대부분의 프로그래머는 다음을 수행합니다.

foo(int weight, int height, int age = 0, string gender = null);

이제 리팩토링이 필요하지 않으며이 기능은 레거시 모드에서 실행될 수 있습니다. gender 는 null 일 .

특정 gender값 에 대한 호출 은 이제 HARDCODED 입니다 age. 이 예제와 같이 :

 foo(10,10,0,"female");

프로그래머는 함수 정의를 보았는데 age, 기본값이 0있고 그 값을 사용하는 것을 보았습니다 .

이제의 기본값 age 함수 정의에서 은 완전히 쓸모가 없습니다.

명명 된 매개 변수를 사용하면이 문제를 피할 수 있습니다.

foo(weight: 10, height: 10, gender: "female");

새 매개 변수를 추가 할 수 있으며 추가 foo된 순서에 대해 걱정할 필요가 없으며 설정 한 기본값이 실제로는 기본값임을 알고 기본값을 변경할 수 있습니다.

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