문제

따라서 언어가 더 높은 순서의 프로시저를 제공하는 경우 프로시저를 반환하는 프로시저를 가질 수 있습니다.다음과 같은 것 :

(define (Proc a b c)
  (lambda (x) ( #| method body here in terms of a b c and x |# )))

새 프로시저를 만들려면 다음과 같이 하면 됩니다.

(define ProcA (Proc a1 b1 c1)) ; Would create ProcA that has 1 argument

고차 절차를 지원하지 않는 언어에서도 유사한 작업을 정의하여 수행할 수 있습니다. Proc 3개의 인수 대신 4개의 인수를 사용하고 이 프로시저를 호출하여 정의합니다. ProcA, 좋다:

(define (Proc a b c x) ( #| method body -- does not return any procedure |# )
(define (ProcA x) (Proc a1 b1 c1 x))

그렇다면 고차 절차에 대해 왜 그렇게 많은 모호함이 있습니까?뭔가 빠졌나요?

도움이 되었습니까?

해결책

다른 함수를 반환하는 함수는 두 인수를 취하는 함수와 동일하다는 것이 좋습니다. 이것을 "카레 링"이라고합니다. 다른 방법으로, A에서 B까지의 함수는 논리적 의미의 증거이며, A는 B를 의미한다는 것입니다.

A => B.

알다시피, A가 B에 C를 암시한다는 것을 암시하면 A와 B는 C, 또는 :

(A => (B => C)) <==> ((A, B) => C)

그러나 고차 함수가 반드시 다른 함수를 반환하는 함수는 아닙니다. 고차 기능은 다른 기능을 인수로 취하는 함수입니다.. 이것은 중요한 차이이며 HOF는 엄청나게 강력한 프로그래밍 도구입니다.

예를 들어이 haskell 기능을 고려하십시오.

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : (map f xs)

이 고차 기능은 기능을 취합니다 f 목록의 모든 요소에 적용합니다. HOF가없는 언어에서는이 기능이 루프 또는 유사한 일로 수행하는 작업을 수행하지만 HOF가있는 언어로는 전화 할 수 있습니다. f 목록의 모든 요소에 대해 다음과 같은 간단한 통화가 있습니다.

map f myList

물론, 언어로 된 제어 구조는 당신에게 고차 기능을 근사 할 수 있지만 고차 기능이있는 언어를 사용하면 자신의 제어 구성을 발명 할 수 있습니다.. 제도는 확실히 자격이 있습니다.

다른 팁

나는 여기서 논쟁을 되풀이하려고하지는 않지만 기능 프로그래밍이 중요한 이유, John Hughes는 고차 기능이 프로그램의 일부를 "함께 붙잡는"방법을 제공하여 코드를 더 쉽게 재사용 할 수 있기 때문에 유용하다고 주장합니다. 이 예제는 더 이상 많이 사용되지 않는 매우 오래된 언어로하지만 여전히 따르기 쉽고 설득력이 있습니다. John의 논문을 읽는 것은 당신의 질문에 대한 자세한 답을 얻는 좋은 방법입니다.

이것은 타당성보다 사고 방식에 관한 것입니다. 기능을 일류 시민으로 취급하고 다른 기능을 만들기 위해 기능에서 작동하는 기능 측면에서 생각할 수 있습니다.

분명히 당신은 다른 언어로 이것을하거나 시뮬레이션 할 수 있지만, 구문 메커니즘이 아니라면 일종의 추가 또는 해킹으로 취급됩니다.

좋아요,하지만 두 번째 예에서는 미리 정지 된 목록으로 컴파일 시간에 해당 절차를 만들고 있습니다. a1, b1, 그리고 c1. 첫 번째 예에서는 전화 할 때 런타임에 만들고 있습니다. ProcA, 당신은 원하는만큼 많은 다른 것들을 만들 수 있으므로 훨씬 더 흥미로운 일을 할 수 있습니다.

배열을 통해 변환 함수 또는 정렬 알고리즘을 생각해보십시오. 이제 기능의 사용자가 기능을 인수로 전달하여 기능의 동작을 지정할 수 있도록 실제로 유연하게 만들고 싶습니다.

다음 절차 프로토 타입으로 정렬 알고리즘을 작성한다고 가정 해 봅시다.

sort(Array a, void (*fn)(a::element_type, a::element_type));

해당 함수의 사용자는 하강 또는 오름차순 순서를 원하는 경우 적절한 FN을 통과시켜 지정할 수 있습니다.

이를 올바르게 시뮬레이션하려면 내부 클래스가 필요합니다.첫 번째 경우 Proc는 a, b 및 c에 대해 닫혀 있습니다.두 번째 경우 ProcA 호출자는 a1, b1 및 c1이 다른 프로시저에 전달되는 방식을 제어할 수 없으며 x만 제어할 수 있습니다.따라서 a1, b1 및 c1을 제어하는 ​​방식은 더 높은 범위(모듈 수준 등)에서 변수를 사용하므로 함수가 순수하지 않게 됩니다.이 경우 호출 전체에 동일한 인수가 주어지면 ProcA가 동일한 결과를 반환한다고 보장할 수 없습니다.Proc과 마찬가지로 동일한 인수로 호출하면 동일한 결과가 발생한다는 것을 항상 확신할 수 있습니다.

예를 들어 선택 상자를 사용할 때 JavaScript에서 고차 함수를 사용합니다. 옵션이 선택 될 때 호출 될 함수를 전달할 수 있습니다. 저의 유일한 차이점은 내 코드를 단순화하고 중복성이 줄어든다는 것입니다.

나는 다른 언어로 더 높은 언어로 똑같은 것을 봅니다. 더 높은 차수 기능을 지원합니다. 코드를 정리하는 방법을 살펴볼 수 있기 때문에, 이중화 할 수있는 중복성이있는 곳에서 차이를 수행 할 수 있습니다. 기능.

C#이 이것을 지원하면, 나는 그것이 더 주류라는 것을 알았습니다. :)

함수가 함수를 수락하고/또는 반환하면이를 고차 기능 (HOF). C, C ++ 또는 Java에서 나오는 경험이없는 프로그래머의 경우 고차 기능이 마술처럼 들리지만 매우 간단합니다. 2 + 3의 결과를 반환하는 간단한 기능을 상상해보십시오.

(define (foo) (+ 2 3)) ;; (foo) => 5

그것은 지루한 기능입니다. 항상 2에서 3을 추가합니다. 일반화하면 3에 2를 추가 할뿐만 아니라 사용자가 제공 한 숫자에 2를 추가하면 어떻게됩니까?

(define (foo n) (+ 2 n)) ;; (foo 10) => 12

언어가 고차 기능을 지원하지 않으면 기능과 값 (예 : 숫자, 부울, 목록)이 두 가지 다른 것이라고 생각해야합니다. 하지만 기능적 프로그래밍 (fp) 그들 사이의 구별이 흐려집니다. 함수와 값의 유일한 차이점은 함수를 호출 할 수 있다는 것입니다. 2 또는 #t 또는 '(a b c): 당신은 그것을 인수로 주거나 함수에서 돌아 오거나 변수에 저장하거나 목록에 넣을 수 있습니다. 예를 들어, 우리의 작은 기능을 더 일반화하겠습니다. n, 그러나 2를 곱하십시오 n, 또는 두 숫자를 수락하는 다른 함수를 적용하십시오.

(define (foo f n) (f 2 n))
;; (foo + 10) => 12
;; (foo * 10) => 20
;; (foo expt 10) => 1024

함수가 숫자 또는 문자열이 처리되는 방식으로 처리 될 수 있다는 것을 알면 익명 기능 (FP 전문 용어에서“람다”라고 함)는 완전히 의미가 있습니다. 익명의 함수는 실제로 일반적인 이름의 함수보다 더 기본적이고 "정상"입니다. 이름이 지정된 기능은 변수에 여러 번 사용하기 위해 변수에 넣는 것처럼 변수에 넣는 익명 함수 일뿐입니다.

(+ 2 2) ;; is no different from:
(let ((a 2)) (+ a a))
(lambda (x y) (* x y)) ;; is no different from:
(define (foo x y) (* x y)) ;; which is an abbreviation for:
(define foo (lambda (x y) (* x y))).

따라서 HOF는 기능을 일반화하여 매우 유연하게 만들 수 있습니다. 당신의 기능을 보면 그 뒤에있는 논리를 볼 수 있습니다. 무엇 그러면 데이터에서 작동합니다 다른 것 아마도 아마도. 두 개의 숫자를 함께 추가하면 숫자를 곱하거나 차감하거나 다른 숫자를 곱하거나 지수를 내릴 수 있습니다. 매번 모든 경우에 대해 새 기능을 작성하는 대신 기능이어야하는 추가 매개 변수 만 수락 할 수 있습니다.

FP에서는 예를 들어 목록을 조작 할 때 항상 HOF를 사용합니다. 3 기능은 FP의 빵과 버터입니다. map, filter 그리고 foldl. map 1 인수가있는 함수를 허용 하고이 함수를 목록의 모든 요소에 적용하고 변경된 요소가있는 새 목록을 반환합니다. filter 1 인수로 술어 (부울을 반환하는 함수)를 수락하고 목록의 모든 요소에 술어를 적용하며 제거 된 술어를 만족하지 않는 요소로 새 목록을 반환합니다.

(map (lambda (n) (+ n 1)) '(1 2 3 4 5) ;; '(2 3 4 5 6)
(define (foo n) (+ n 1))
(map foo '(1 2 3 4 5)) ;; '(2 3 4 5 6)
(filter (lambda (n) (> n 3)) '(1 2 3 4 5)) ;; '(4 5)
(define (bar n) (> n 3))
(filter bar '(1 2 3 4 5)) ;; '(4 5)

1- 아티 리티 함수 목록이 있다고 상상해보십시오. 결과의.

(let ((xs (list (lambda (x) (+ x 1))
                (lambda (x) (* x 2))
                (lambda (x) (- x)))))
  (map (lambda (f) (f 10)) xs)) ;; => (11 20 -10)

결론: 프로그래밍 언어가 기능적 프로그래밍 개념을 올바르게 지원할 때 고차 기능은 유연성과 일반성을 허용하여 코드를보다 강력하게 만들고 (다양한 사용 사례에 동일한 기능을 사용할 수 있음) 간결한 (10 버전의 1 버전을 작성할 필요가 없습니다. 기능). 일부 고차 기능은 기능 프로그래밍에 크게 사용되므로 저수준 및 장점을 제거하고 대신 모든 것을 수행하는 라인 라이너를 작성합니다.

메모: foldl, "왼쪽 접기"또는 "왼쪽 감소"와 동일하게 더 강력합니다. 정말로 관심이 있고 시간이 있다면 상반기를 읽으십시오. 감소를 사용한 내 대답. Scheme/Rack을 위해 작성되지는 않았지만 일반적인 LISP/EMACS LISP를 위해서는 여전히 Fold/Reduce의 아이디어를 이해할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top