카레와 다양한 기능을 동시에 할 수 있습니까?


13

카레 함수와 가변 함수를 둘 다 동적으로 유형화 된 함수형 프로그래밍 언어로 사용할 수있게하려고하는데 이것이 가능한지 궁금합니다.

의사 코드는 다음과 같습니다.

sum = if @args.empty then 0 else @args.head + sum @args.tail

아마도 모든 주장을 요약해야합니다. 그런 다음 sum자체가 숫자로 취급되면 결과는 0입니다. 예를 들어

sum + 1

+숫자에서만 작동 한다고 가정하면 1과 같습니다 . 그러나 sum == 0사실 조차도 sum, 예를 들어 내가 선언하면, 얼마나 많은 인수가 주어 졌는지 (따라서 "부분적으로 적용됨"과 "가변적"임)에도 불구하고 여전히 가치와 기능적 속성을 유지합니다

g = sum 1 2 3

다음 g에 동일 6하지만, 우리는 여전히 더 적용 할 수 있습니다 g. 예를 들어, g 4 5 == 15사실입니다. 이 경우 정수로 취급 할 때 동일한 값을 생성하지만 내부에 다른 코드가 포함되어 있기 때문에 객체 g를 리터럴로 바꿀 수 없습니다 6.

이 디자인이 실제 프로그래밍 언어로 사용되는 경우 혼란이나 모호함이 발생합니까?


1
엄밀히 말하면, 카레를 언어의 기초로 사용한다는 것은 모든 기능이 단항 함을 의미합니다. variadic 기능은없고 이진 기능도 없습니다! 그러나 해당 언어의 프로그램은 여전히 여러 개의 인수를 취하는 것처럼 보이며 이는 일반적인 함수와 마찬가지로 가변적 인 기능을 수행합니다.
Kilian Foth

그런 다음 제 질문은 "객체가 동시에 함수와 비 기능 값이 될 수 있습니까?" 위의 예에서, sum이다 0인수없이 재귀 인수 자체를 호출합니다.
Michael Tsang

그 일이 reduce아닌가요?
ratchet freak

1
당신이 사용하고있는 기능을 살펴보십시오 args: empty, head,와 tail. 그것들은 모두 목록 기능이므로, 더 쉽고 더 간단한 방법은 가변성 물건이있는 목록을 사용하는 것입니다. (그래서 sum [1, 2, 3]대신 sum 1 2 3)
Michael Shaw

답변:


6

varargs는 어떻게 구현할 수 있습니까? 인수 목록의 끝을 알리는 메커니즘이 필요합니다. 이것은 둘 중 하나 일 수 있습니다

  • 특수 종결 자 값 또는
  • 추가 매개 변수로 전달 된 vararg 목록의 길이

이러한 메커니즘은 커링의 맥락에서 varargs를 구현하는 데 사용될 수 있지만 올바른 타이핑이 주요 문제가됩니다. sum: ...int -> int이 함수가 카레를 사용한다는 점을 제외하고 함수를 다루고 있다고 가정 해 봅시다 (따라서 sum: int -> ... -> int -> int인수의 수를 모르는 것을 제외하고 는 실제로 더 비슷한 유형이 있습니다 ).

사례 : 종결 자 값 : end특수 종결자가 T되고 유형입니다 sum. 우리는 이제 end함수에 적용하면 다음을 반환 한다는 것을 알고 있습니다 sum: end -> int. int에 적용된 것은 또 다른 sum-like 함수를 얻습니다 sum: int -> T. 따라서 T이러한 유형의 결합은 다음과 같습니다 T = (end -> int) | (int -> T).. 대체하여 T, 우리는 다음과 같은 가능한 다양한 종류의 수 end -> int, int -> end -> int, int -> int -> end -> int, 등 그러나, 대부분의 타입 시스템이 같은 유형을 수용하지 않는합니다.

사례 : 명시 적 길이 : vararg 함수의 첫 번째 인수는 varargs의 수입니다. 그래서 sum 0 : int, sum 1 : int -> int, sum 3 : int -> int -> int -> int등이 어떤 종류의 시스템에서 지원의 예입니다 의존 입력 . 사실, 인수의 수는 유형 매개 변수가 아닌 일반 매개 변수가 될 것 - 그것은 런타임 값에 의존하는 함수의 인수에 대응하기위한 이해가되지 것, s = ((sum (floor (rand 3))) 1) 2분명히 잘못 입력 한 것입니다 : 하나이 평가하여 s = ((sum 0) 1) 2 = (0 1) 2, s = ((sum 1) 1) 2 = 1 2또는 s = ((sum 2) 1) 2 = 3.

실제로 이러한 기술은 오류가 발생하기 쉬우 며 일반적인 유형 시스템에 (의미있는) 유형이 없으므로 사용해서는 안됩니다. 대신 값 목록을 하나의 매개 변수로 전달하십시오 sum: [int] -> int.

예, 물체가 함수와 값으로 나타날 수 있습니다 (예 : 강압 형 시스템에서). 두 개의 강제 변환이 sum있는을 보자 SumObj.

  • coerce: SumObj -> int -> SumObjsum함수로 사용 가능
  • coerce: SumObj -> int 결과를 추출 할 수 있습니다.

기술적으로 이는 상기 터미네이터 케이스 값의 변동과 인 T = SumObjcoerce유형에 대한 해제되고 래퍼. 많은 객체 지향 언어에서 C ++와 같이 연산자 오버로드를 사용하여 간단하게 구현할 수 있습니다.

#include <iostream>
using namespace std;

class sum {
  int value;
public:
  explicit sum() : sum(0) {}
  explicit sum(int x) : value(x) {}
  sum operator()(int x) const { return sum(value + x); }  // function call overload
  operator int() const { return value; } // integer cast overload
};

int main() {
  int zero = sum();
  cout << "zero sum as int: " << zero << '\n';
  int someSum = sum(1)(2)(4);
  cout << "some sum as int: " << someSum << '\n';
}

멋진 답변! 포장에서 varargs를 포장 할 때의 단점은 카레의 부분적 적용을 상실한다는 것입니다. 키워드 함수 ..., force=False)를 사용 하여 초기 기능을 강제로 적용 하는 Python 버전의 종결 자 접근 방식으로 놀았습니다 .
ThomasH

와 같이 목록을 취하는 함수를 부분적으로 적용하는 고유 한 고차 함수를 만들 수 있습니다 curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys).
Jack

2

당신은보고 할 수 있습니다 하스켈의 printf의 구현 과 함께, 그것이 작동하는 방법의 설명 . 후자의 페이지에는 Oleg Kiselyov의 이런 종류의 일에 관한 논문에 대한 링크가 있습니다. 실제로 기능적 언어를 디자인하는 경우 Oleg의 웹 사이트 는 반드시 읽어야합니다.

제 생각에는 이러한 접근 방식은 약간의 해킹이지만 가능하다는 것을 보여줍니다. 그러나 언어에 전적으로 의존하는 타이핑 기능이 있다면 훨씬 간단합니다. 정수 인수를 합산하는 가변 함수는 다음과 같습니다.

type SumType = (t : union{Int,Null}) -> {SumType, if t is Int|
                                         Int,     if t is Null}
sum :: SumType
sum (v : Int) = v + sum
sum (v : Null) = 0

명시적인 이름을 제공하지 않고 재귀 유형을 정의하기위한 추상화는 이러한 함수를보다 쉽게 ​​작성할 수있게합니다.

편집 : 물론, 나는 단지 질문을 다시 읽고 동적으로 유형이 지정된 언어 를 말했는데 , 그 시점에서 분명히 유형 역학이 실제로 관련이 없으므로 @amon의 대답에는 아마도 필요한 모든 것이 포함되어있을 것입니다. 글쎄, 정적 언어로 방법을 궁금해하는 사람이 있다면 이것을 여기에 남겨 두겠습니다.


0

다음은 Python의 선택적 인수를 활용하여 @amon의 "종료 자"접근 방식을 사용하는 Python3의 가변 함수를 카레 링하는 버전입니다.

def curry_vargs(g):
    actual_args = []
    def f(a, force=False):
        nonlocal actual_args
        actual_args.append(a)
        if force:
            res = g(*actual_args)
            actual_args = []
            return res
        else:
            return f
    return f

def g(*args): return sum(args)
f = curry_vargs(g)
f(1)(2)(3)(4,True) # => 10

반환 된 함수 f는 외부 범위에 바인딩 된 배열에서 연속적인 호출로 전달 된 인수를 수집합니다. force인수가 참일 때만 지금까지 수집 된 모든 인수와 함께 원래 함수가 호출됩니다.

이 구현의주의 사항은 항상 첫 번째 인수를 전달해야 f하므로 모든 인수가 바인딩되고 빈 인수 목록으로 만 호출 할 수있는 함수 인 "thunk"를 만들 수 없다는 것입니다 (그러나 이것이 카레의 전형적인 구현).

또 다른 경고는 잘못된 인수 (예 : 잘못된 유형)를 전달하면 원래 기능을 다시 사용해야한다는 것입니다. 내부 배열을 재설정하는 다른 방법은 없습니다.이 기능은 커리 기능을 성공적으로 실행 한 후에 만 ​​수행됩니다.

괄호가없는 함수에 대한 참조가 내부 함수 객체로 평가되면 "객체가 함수와 비 함수 값이 동시에 될 수 있습니까?"라는 간단한 질문이 파이썬에서 구현 될 수 있는지 모르겠습니다. . 이것이 임의의 값을 반환하도록 구부릴 수 있는지 모르겠습니다.

Lisp 기호는 값과 함수 값을 동시에 가질 수 있으므로 Lisp에서는 쉬울 것입니다. 기능 값은 기호가 기능 위치 (목록의 첫 번째 요소)에 나타날 때 간단히 선택됩니다.

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