함수를 호출 할 때 함수의 매개 변수 이름을 모르는 경우


13

여기 당신의 생각을 듣고 싶은 프로그래밍 / 언어 문제가 있습니다.

우리는 언어 구문의 일부가 아니지만 코드를 더 읽기 쉽게 만드는 역할을하는 대부분의 프로그래머가 따라야하는 규칙을 개발했습니다. 물론 이것들은 항상 논쟁의 여지가 있지만, 대부분의 프로그래머들이 동의할만한 핵심 개념이 적어도 있습니다. 변수의 이름을 적절하게 지정하고, 일반적으로 이름을 지정하고, 긴 함수, 캡슐화 등을 피하면서 행을 지나치게 길게 만들지 마십시오.

그러나 아직 내가 언급 한 사람을 찾지 못했고 그중 가장 큰 문제 일 수있는 문제가 있습니다. 함수를 호출 할 때 인수가 익명이되는 문제입니다.

함수는 일반적으로 프로그래밍에서하는 것보다 훨씬 엄격한 정의를 갖기 때문에 f (x)가 명확한 의미를 갖는 수학에서 나옵니다. 수학의 순수한 함수는 프로그래밍에서 할 수있는 것보다 훨씬 적은 작업을 수행 할 수 있으며 훨씬 더 우아한 도구이며 일반적으로 하나의 인수 (보통 숫자) 만 취하며 항상 하나의 값 (보통 숫자)을 반환합니다. 함수가 여러 인수를 사용하는 경우 거의 항상 함수 도메인의 추가 차원 일뿐입니다. 다시 말해, 하나의 주장은 다른 것보다 중요하지 않습니다. 그것들은 명시 적으로 정렬되어 있지만, 그 외에는 의미 론적 순서가 없습니다.

그러나 프로그래밍에서 우리는 더 많은 자유 함수를 정의 할 수 있으며,이 경우에는 좋은 것이 아니라고 주장합니다. 일반적인 상황에서 다음과 같이 정의 된 기능이 있습니다.

func DrawRectangleClipped (rectToDraw, fillColor, clippingRect) {}

정의를 살펴보면 함수가 올바르게 작성되면 무엇이 무엇인지 완벽하게 알 수 있습니다. 함수를 호출 할 때 IDE / 편집기에서 인텔리전스 / 코드 완성 마법을 사용하여 다음 인수가 무엇인지 알려줄 수도 있습니다. 하지만 기다려. 실제로 전화를 걸 때 필요한 경우 여기에없는 것이 있습니까? 코드를 읽는 사람에게는 IDE의 이점이 없으며 정의로 건너 뛰지 않으면 인수로 전달 된 두 사각형 중 어느 것이 무엇에 사용되는지 알 수 없습니다.

문제는 그 이상으로 진행됩니다. 인수가 지역 변수에서 나온 경우 변수 이름 만 보이기 때문에 두 번째 인수가 무엇인지 모르는 상황이있을 수 있습니다. 이 코드 줄을 예로 들어 보겠습니다.

DrawRectangleClipped(deserializedArray[0], deserializedArray[1], deserializedArray[2])

이것은 다른 언어로 다양한 범위로 완화되지만 엄격하게 유형이 지정된 언어로도 변수를 의미있게 명명하더라도 변수에 함수에 전달 할 때 변수의 유형을 언급하지조차 않습니다.

일반적으로 프로그래밍과 마찬가지로이 문제에 대한 많은 잠재적 솔루션이 있습니다. 많은 사람들이 이미 대중적인 언어로 구현되어 있습니다. 예를 들어 C #의 명명 된 매개 변수 그러나 내가 아는 모든 것은 심각한 단점이 있습니다. 모든 함수 호출에서 모든 매개 변수의 이름을 지정해도 코드를 읽을 수는 없습니다. 평범한 텍스트 프로그래밍이 우리에게 제공 할 가능성이 점점 커지고있는 것 같습니다. 거의 모든 영역에서 JUST 텍스트로 이동했지만 여전히 동일하게 코딩됩니다. 코드에 더 많은 정보가 필요합니까? 더 많은 텍스트를 추가하십시오. 어쨌든, 이것은 약간의 접선을 얻고 있으므로 여기서 멈출 것입니다.

두 번째 코드 스 니펫에 대한 한 가지 대답은 먼저 명명 된 변수로 배열의 압축을 풀고 사용합니다.하지만 변수의 이름은 많은 것을 의미 할 수 있으며 호출 된 방식이 반드시 필요한 방식을 알려주지는 않습니다. 호출 된 함수의 컨텍스트에서 해석됩니다. 로컬 범위에는 의미 상으로 나타내는 것이기 때문에 leftRectangle과 rightRectangle이라는 두 개의 사각형이있을 수 있지만, 함수에 제공 될 때 나타내는 것으로 확장 할 필요는 없습니다.

실제로 변수의 이름이 호출 된 함수의 컨텍스트에서 명명 된 경우 해당 함수 호출로 할 수있는 것보다 적은 정보를 도입 할 수 있으며 코드를 더 나쁜 코드로 만드는 경우에는 어느 정도 수준에서 발생합니다. rectForClipping에 저장하는 사각형을 생성하는 프로 시저가 있고 rectForDrawing을 제공하는 다른 프로 시저가있는 경우 DrawRectangleClipped에 대한 실제 호출은 식입니다. 새로운 것을 의미하지 않는 선은 컴퓨터가 이미 이름으로 설명 했음에도 불구하고 원하는 것을 정확하게 알고 있다는 것입니다. 이것은 좋지 않습니다.

나는 이것에 대한 신선한 관점을 듣고 싶습니다. 이 문제를 처음으로 고려한 사람이 아니라고 확신하므로 어떻게 해결됩니까?


2
정확한 문제가 무엇인지 혼란 스럽습니다 ... 여기에 몇 가지 아이디어가있는 것 같습니다. 어떤 것이 당신의 주요 포인트인지 확실하지 않습니다.
FrustratedWithFormsDesigner

1
함수에 대한 문서는 인수가 무엇을하는지 알려줍니다. 코드를 읽는 누군가가 문서를 가지고 있지 않을 수도 있지만 실제로 코드의 기능과 코드에서 추출한 의미가 무엇인지 알지 못한다고 생각할 수도 있습니다. 독자가 코드가 올바른지 알아야하는 모든 상황에서 문서가 필요합니다.
Doval

3
@Darwin 함수형 프로그래밍에서 모든 함수에는 여전히 1 개의 인수 만 있습니다. "다중 인수"를 전달해야하는 경우, 매개 변수는 일반적으로 튜플 (주문하려는 경우) 또는 레코드 (원치 않는 경우)입니다. 또한 언제든지 특수 버전의 함수를 구성하는 것이 쉽지 않으므로 필요한 인수 수를 줄일 수 있습니다. 거의 모든 기능 언어가 튜플과 레코드에 대한 구문을 제공하므로 값을 묶는 것은 고통스럽지 않으며 무료로 구성 할 수 있습니다 (튜플을 반환하는 함수와 튜플을 반환하는 함수를 연결할 수 있음)
Doval

1
@ Bergi 사람들은 순수한 FP에서 훨씬 더 일반화하는 경향이 있으므로 함수 자체는 일반적으로 작고 더 많다고 생각합니다. 그래도 벗어날 수 있습니다. Haskell 및 갱단과 실제 프로젝트를 수행 한 경험이 많지 않습니다.
다윈

4
대답은 "변수 이름을 'deserializedArray'로 지정하지 마십시오"입니까?
whatsisname

답변:


10

함수가 자주 사용되는 방식이 코드 작성, 특히 코드 읽기의 혼란스러운 부분 일 수 있다는 데 동의합니다.

이 문제에 대한 답은 언어에 부분적으로 달려 있습니다. 언급했듯이 C #에는 매개 변수의 이름이 지정되어 있습니다. 이 문제에 대한 Objective-C의 솔루션에는보다 구체적인 메소드 이름이 포함됩니다. 예를 들어, stringByReplacingOccurrencesOfString:withString:명확한 매개 변수가있는 방법입니다.

Groovy에서 일부 함수는 맵을 사용하여 다음과 같은 구문을 허용합니다.

restClient.post(path: 'path/to/somewhere',
            body: requestBody,
            requestContentType: 'application/json')

일반적으로 함수에 전달하는 매개 변수 수를 제한하여이 문제를 해결할 수 있습니다. 2-3은 좋은 한계라고 생각합니다. 함수에 더 많은 매개 변수가 필요한 것으로 보이면 디자인을 다시 생각하게됩니다. 그러나 이것은 일반적으로 대답하기가 더 어려울 수 있습니다. 때로는 함수에서 너무 많은 일을하려고합니다. 때로는 매개 변수를 저장할 클래스를 고려하는 것이 좋습니다. 또한 실제로 많은 수의 매개 변수를 사용하는 함수에는 일반적으로 많은 매개 변수가 선택 사항으로 포함되어 있습니다.

Objective-C와 같은 언어에서도 매개 변수 수를 제한하는 것이 좋습니다. 한 가지 이유는 많은 매개 변수가 선택 사항이기 때문입니다. 예를 들어 rangeOfString : 및 NSString 의 변형을 참조하십시오 .

Java에서 자주 사용하는 패턴은 유동 스타일 클래스를 매개 변수로 사용하는 것입니다. 예를 들면 다음과 같습니다.

something.draw(new Box().withHeight(5).withWidth(20))

이것은 클래스를 매개 변수로 사용하고 유창한 스타일 클래스를 사용하여 쉽게 읽을 수있는 코드를 만듭니다.

위의 Java 스 니펫은 매개 변수 순서가 명확하지 않은 경우에도 도움이됩니다. 우리는 일반적으로 X가 Y보다 먼저 오는 좌표로 가정합니다. 나는 일반적으로 너비 앞의 높이를 규칙으로 보지만 여전히 명확하지 않습니다 ( something.draw(5, 20)).

나는 또한 같은 기능을 보았지만 drawWithHeightAndWidth(5, 20)너무 많은 매개 변수를 취할 수 없거나 가독성을 잃기 시작했습니다.


2
Java 예제를 계속하면 순서가 까다로울 수 있습니다. 예를 들어, AWT에서 다음 생성자 비교 : Dimension(int width, int height)GridLayout(int rows, int cols)(행수가 높이 의미는 GridLayout먼저 높이 갖는 Dimension폭).
Pierre Arlaud

1
이러한 불일치는 매우 PHP (와 비판 한 eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design 예를 들어) : array_filter($input, $callback)array_map($callback, $input), strpos($haystack, $needle)array_search($needle, $haystack)
피에르 Arlaud

12

대부분 함수, 매개 변수 및 인수의 이름을 잘 지정하여 해결됩니다. 그러나 이미 조사한 결과 결함이 있음을 발견했습니다. 호출 컨텍스트와 호출 컨텍스트 모두에서 적은 수의 매개 변수로 함수를 작게 유지하여 이러한 결함을 대부분 완화 할 수 있습니다. 호출하는 함수가 한 번에 여러 작업을 수행하려고하기 때문에 문제가 있습니다. 기본 사각형 지정, 클리핑 영역 지정, 그리기 및 특정 색상으로 채우기.

이것은 형용사 만 사용하여 문장을 쓰려고하는 것과 같습니다. 거기에 더 많은 동사 (함수 호출)를 넣고 문장의 주제 (객체)를 작성하면 읽기 쉽습니다.

rect.clip(clipRect).fill(color)

해도 clipRectcolor끔찍한 이름을 가지고 (그들은 안), 당신은 여전히 상황에서 자신의 유형을 식별 할 수 있습니다.

deserialized 예제는 호출 컨텍스트가 한 번에 너무 많은 일을하려고하기 때문에 문제가 있습니다. 직렬화 및 무언가 그리기. 이해하기 쉬운 이름을 지정하고 두 가지 책임을 명확하게 구분해야합니다. 최소한 다음과 같은 것이 있습니다.

(rect, clipRect, color) = deserializeClippedRect()
rect.clip(clipRect).fill(color)

인간이 문맥과 의미를 식별하는 데 필요한 중간 단계를 건너 뛰고 너무 간결하게 시도하면 많은 가독성 문제가 발생합니다.


1
의미를 명확히하기 위해 여러 함수 호출을 문자열로 묶는 아이디어가 마음에 들지만 문제를 중심으로 춤을 추는 것이 아닙니다. 기본적으로 "문장을 쓰고 싶지만 내가 고소하는 언어로 가장 근접한 단어 만 사용할 수 있습니다"
Darwin

@Darwin IMHO 프로그래밍 언어를보다 자연스럽게 만들면 개선 될 수 없습니다. 자연어는 매우 모호하며 문맥에서만 이해하고 실제로는 확신 할 수 없습니다. 모든 용어는 (이상적으로는) 문서와 사용 가능한 소스를 가지고 있고 구조를 명확하게하는 괄호와 점이 있으므로 문자열 함수 호출은 더 좋습니다.
maaartinus

3

실제로는 더 나은 디자인으로 해결됩니다. 잘 작성된 함수가 2 개 이상의 입력을받는 것은 매우 드문 일이며, 발생하는 경우 많은 입력이 일부 응집 된 묶음으로 집계되지 않는 경우는 드 '니다. 이렇게하면 함수를 분해하거나 매개 변수를 집계하는 것이 쉬워 지므로 함수를 너무 많이 수행하지 않아도됩니다. 하나는 두 개의 입력을 가지고 있으며, 이름을 지정하기 쉽고 어떤 입력이 어느 것인지 명확하게 알 수 있습니다.

내 장난감 언어는 이것을 다루는 문구 의 개념을 가지고 있었고, 다른 자연 언어 중심의 프로그래밍 언어는 그것을 다루는 다른 접근법을 가지고 있었지만 모두 다른 단점이 있습니다. 또한 문구조차도 함수의 이름이 더 좋게 만드는 멋진 구문에 지나지 않습니다. 많은 입력을받을 때 항상 좋은 함수 이름을 만드는 것은 어려울 것입니다.


문구는 실제로 한 단계 발전한 것처럼 보입니다. 일부 언어의 기능은 비슷하지만 널리 사용되지는 않습니다. 말할 것도없이, 매크로를 제대로 사용하지 않은 C (++) 순수 주의자들로부터 나오는 모든 매크로 증오와 함께, 우리는 대중적인 언어로 이러한 기능을 갖지 못할 수도 있습니다.
다윈

의 일반적인 주제에 오신 것을 환영합니다 도메인 특정 언어 , 뭔가 내가 정말 더 많은 사람들이 이점의 ... (1) 이해 텐데
Izkata

2

에서 자바 스크립트 (또는 ECMAScript를 ) 예를 들어, 많은 프로그래머는 익숙한 성장했다

단일 익명 객체에서 명명 된 객체 속성 집합으로 매개 변수를 전달합니다.

그리고 프로그래밍 실습으로 프로그래머에서 라이브러리로, 거기에서 그것을 좋아하고 그것을 사용하고 더 많은 라이브러리를 작성하는 다른 프로그래머에게 전달되었습니다.

전화하는 대신

function drawRectangleClipped (rectToDraw, fillColor, clippingRect)

이처럼 :

drawRectangleClipped(deserializedArray[0], deserializedArray[1], deserializedArray[2])

올 바르고 올바른 스타일 인

function drawRectangleClipped (params)

이처럼 :

drawRectangleClipped({
    rectToDraw: deserializedArray[0], 
    fillColor: deserializedArray[1], 
    clippingRect: deserializedArray[2]
})

귀하의 질문과 관련하여 유효하고 정확 하며 훌륭합니다.

물론, 이것에 대한 적절한 조건이 있어야합니다-Javascript에서는 C보다 훨씬 더 실용적입니다. javascript에서는 이제 XML에 비해 가벼운 것으로 널리 사용되는 널리 사용되는 구조 표기법이 탄생했습니다. JSON이라고합니다 (이미 들어 보셨을 것입니다).


구문을 확인하기 위해 해당 언어에 대해 충분히 알지 못하지만 전체적으로이 게시물을 좋아합니다. 꽤 우아해 보입니다. +1
IT Alex

종종 이것은 일반적인 인수와 결합됩니다. 예를 들어이 함수와 같이 1-3 개의 인수와 params(선택적 인수를 포함하고 종종 자체를 선택적으로 포함) 있습니다 . 이것은 많은 인수를 가진 함수를 이해하기 쉽게 만듭니다 (제 예에서는 2 개의 필수 인수와 6 개의 옵션 인수가 있습니다).
maaartinus

0

objective-C를 사용해야합니다. 다음은 함수 정의입니다.

- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject

그리고 여기에 사용됩니다 :

[someObject performSelector:someSelector withObject:someObject2 withObject:someObject3];

루비가 비슷한 구성을 가지고 있다고 생각하며 키-값 목록을 사용하여 다른 언어로 시뮬레이션 할 수 있습니다.

Java의 복잡한 함수의 경우 함수 표현에서 더미 변수를 정의하고 싶습니다. 왼쪽-오른쪽 예 :

Rectangle referenceRectangle = leftRectangle;
Rectangle targetRectangle = rightRectangle;
doSomeWeirdStuffWithRectangles(referenceRectangle, targetRectangle);

더 많은 코딩처럼 보이지만 예를 들어 leftRectangle을 사용한 다음 나중에 로컬 코드 추출 프로그램으로 코드를 리팩터링 할 수 있습니다.


그 자바 예제에 대해, 나는 그것이 왜 좋은 해결책이 아니라고 생각하는지 질문에 썼습니다. 그것에 대해 어떻게 생각하세요?
다윈

0

나의 접근 방식은 임시 로컬 변수를 만드는 것입니다 -하지만 단지 그들을 호출하지 LeftRectange하고 RightRectangle. 오히려, 나는 더 긴 의미를 전달하기 위해 다소 긴 이름을 사용합니다. 나는 something_rectangle그들의 역할이 매우 대칭 적이 지 않다면 가능한 한 많은 이름을 구별하려고 노력한다 .

예 (C ++) :

auto& connector_source = deserializedArray[0]; 
auto& connector_target = deserializedArray[1]; 
auto& bounding_box = deserializedArray[2]; 
DoWeirdThing(connector_source, connector_target, bounding_box)

그리고 한 줄짜리 래퍼 함수 또는 템플릿을 작성할 수도 있습니다.

template <typename T1, typename T2, typename T3>
draw_bounded_connector(
    T1& connector_source, T2& connector_target,const T3& bounding_box) 
{
    DoWeirdThing(connector_source, connector_target, bounding_box)
}

(C ++을 모른다면 앰퍼샌드를 무시하십시오).

함수가 좋은 설명없이 몇 가지 이상한 일을하면 리팩터링해야합니다!

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