수정 사항은 어떻게 사용하고 어떻게 작동합니까?


87

문서에 대해 약간 혼란스러워서 fix(지금해야 할 일을 이해한다고 생각하지만) 소스 코드를 살펴 보았습니다. 그로 인해 더 혼란스러워졌습니다.

fix :: (a -> a) -> a
fix f = let x = f x in x

이것이 고정 소수점을 정확히 반환하는 방법은 무엇입니까?

명령 줄에서 시도해보기로했습니다.

Prelude Data.Function> fix id
...

그리고 그것은 거기에 달려 있습니다. 이제 공정하게 말하면, 이것은 약간 느린 내 오래된 맥북에 있습니다. 그러나이 함수는 id에 전달 된 모든 것이 동일한 것을 되돌려주기 때문에 계산적 으로 너무 비쌀 수는 없습니다 (CPU 시간을 소모하지 않는다는 것은 말할 것도 없습니다). 내가 도대체 ​​뭘 잘못하고있는 겁니까?


68
장난 대답은 "수정은 실제로 사용되지 않습니다. 바로 거기에 있으므로 fix errorghci를 입력 하고 자신에 대해 기분이 좋아질 수 있습니다 ."
Thomas M. DuBuisson 2011 년

3
@TomMD-재미 있네요. 누군가 나에게 픽스가 무엇을하는지 물어 보면 나는 기분이 나쁘다는 것을 기억할 것이다. :-)
Jason Baker

2
나는 보통 쓰기 fixfix f = f (fix f). 짧고 단순하며 작동하며 수학적 정의와 동일합니다.
newacct 2011 년

20
@newacct, 네, 저도 그렇게 생각합니다. 그러나 여기에있는 것이 더 효율적인 구조로 이어질 수 있습니다. 다음과 같이 평가하면 차이를 확인할 수 있습니다 fix (1:) !! (10^8). 원본은 일정한 메모리에서 수행하고, 선형 메모리를 사용하므로 속도가 상당히 느려집니다. 즉, let을 사용하면 "더 타이트한 매듭"을 사용하여 원형 데이터 구조를 생성 할 수 있지만 사용자는 생성하지 않습니다.
luqui

22
당신도 재창조 할 수있었습니다 fix! fix많은 것을 이해 하는 데 도움이되었습니다 .
fredoverflow 2011

답변:


90

당신은 잘못한 것이 없습니다. fix id무한 루프입니다.

이것이 fix함수의 최소 고정 소수점 을 반환 한다고 말할 때 , 우리는 도메인 이론의 의미에서 의미합니다. 그래서 fix (\x -> 2*x-1)되어 있지 반환하려고 1하지만 때문에, 1그 함수의 고정 된 지점입니다, 그것은 아닌 이상 도메인 주문 하나.

도메인 순서를 한두 단락으로 설명 할 수 없으므로 위의 도메인 이론 링크를 참조하겠습니다. 읽기 쉽고 깨달음을주는 훌륭한 튜토리얼입니다. 나는 그것을 적극 추천합니다.

10,000 피트에서보기 fix의 경우 재귀 아이디어를 인코딩하는 고차 함수입니다 . 표현식이있는 경우 :

let x = 1:x in x

무한 목록이되는 결과는 다음을 [1,1..]사용하여 같은 말을 할 수 있습니다 fix.

fix (\x -> 1:x)

(또는 간단히 fix (1:)), 나에게 고정 된 지점 찾기라고하는 (1:)기능을 IOW는 값이 x되도록 x = 1:x처럼 우리는 위의 정의 .... 정의에서 알 수 있듯이, fix이 아이디어에 지나지 않습니다. 함수로 캡슐화 된 재귀입니다.

이것은 재귀의 진정한 일반 개념이기도 합니다. 다형성 재귀를 사용하는 함수를 포함하여 모든 재귀 함수를 이런 방식으로 작성할 수 있습니다 . 예를 들어 전형적인 피보나치 함수는 다음과 같습니다.

fib n = if n < 2 then n else fib (n-1) + fib (n-2)

다음과 같이 쓸 수 있습니다 fix.

fib = fix (\f -> \n -> if n < 2 then n else f (n-1) + f (n-2))

연습 :의 정의를 확장하여이 fix두 정의 fib가 동일 함 을 보여줍니다 .

그러나 완전한 이해를 위해 영역 이론에 대해 읽어보십시오. 정말 멋진 것입니다.


32
여기에 대해 생각하는 관련 방법이있다 fix id: fix유형의 기능을 소요 a -> a하고 유형의 값을 반환합니다 a. id는 모든 a에 대해 다형성 이기 때문에 fix id유형 a, 즉 가능한 값을 갖습니다. Haskell에서 모든 유형이 될 수있는 유일한 값은 bottom, ⊥이며 종료되지 않는 계산과 구별 할 수 없습니다. 따라서 fix id정확히해야하는 값인 최저 값을 생성합니다. 위험은 fix⊥가 함수의 고정 점이면 정의에 따라 최소 고정 점이므로 fix종료되지 않는다는 것입니다.
John L

4
Haskell의 @JohnL undefined은 모든 유형의 값입니다. 다음 fix과 같이 정의 할 수 있습니다 fix f = foldr (\_ -> f) undefined (repeat undefined)..
didest dec.

1
@Diego 귀하의 코드는 _Y f = f (_Y f).
윌 네스

25

나는 이것을 전혀 이해한다고 주장하지 않지만 이것이 누군가에게 도움이된다면 ...

의 정의를 고려하십시오 fix. fix f = let x = f x in x. 마음 불허 부분이 있다는 것입니다 x으로 정의된다 f x. 그러나 잠시 생각해보십시오.

x = f x

x = fx이므로 x오른쪽에있는 값을 대체 할 수 있습니다 . 그러므로...

x = f . f $ x -- or x = f (f x)
x = f . f . f $ x -- or x = f (f (f x))
x = f . f . f . f . f . f . f . f . f . f . f $ x -- etc.

그래서 트릭은 종료하기 위해 f일종의 구조를 생성해야하므로 나중에 f매개 변수 (?)의 전체 "값"에 대해 신경 쓰지 않고 해당 구조와 패턴을 일치시키고 재귀를 종료 할 수 있습니다.

물론 luqui가 설명하는 것처럼 무한 목록을 만드는 것과 같은 작업을 수행하고 싶지 않은 한.

TomMD의 계승 설명이 좋습니다. 수정의 유형 서명은 (a -> a) -> a. 대한 유형 서명 (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1)(b -> b) -> b -> b, 즉, (b -> b) -> (b -> b). 그래서 우리는 그렇게 말할 수 있습니다 a = (b -> b). 그런 식으로, 수정은 우리의 기능을한다 a -> a, 또는 정말, (b -> b) -> (b -> b)및 유형의 결과를 반환합니다 a, 즉, b -> b즉, 다른 기능!

잠깐, 함수가 아닌 고정 소수점을 반환해야한다고 생각했습니다. 글쎄요, 일종의 (함수가 데이터이기 때문에). 팩토리얼을 찾는 결정적인 함수를 제공했다고 상상할 수 있습니다. 재귀하는 방법을 모르는 함수 (따라서 매개 변수 중 하나는 재귀에 사용되는 함수)를 제공하고 fix재귀하는 방법을 가르쳤습니다.

f나중에 f패턴이 일치하고 종료 될 수 있도록 일종의 구조를 생성 해야한다고 내가 말한 것을 기억 하십니까? 정확하지 않은 것 같습니다. TomMD는 어떻게 확장 x하여 기능을 적용하고 기본 케이스로 나아갈 수 있는지 설명했습니다 . 그의 기능을 위해 그는 if / then을 사용했고 이것이 종료의 원인입니다. 반복 된 교체 후,의 in전체 정의의 일부는 fix결국 x계산 가능하고 완전한 시점 에서 정의되는 것을 중단합니다 .


감사. 이것은 매우 유용하고 실용적인 설명입니다.
kizzx2

17

수정 점을 종료 할 방법이 필요합니다. 예제를 확장하면 끝나지 않을 것이 분명합니다.

fix id
--> let x = id x in x
--> id x
--> id (id x)
--> id (id (id x))
--> ...

다음은 수정을 사용하는 실제 예입니다 (수정을 자주 사용하지 않고 아마 피곤했거나 읽을 수있는 코드에 대해 걱정하지 않았습니다).

(fix (\f h -> if (pred h) then f (mutate h) else h)) q

WTF, 당신은 말한다! 네,하지만 여기에 정말 유용한 몇 가지 사항이 있습니다. 우선, 첫 번째 fix인수는 일반적으로 '재귀'케이스 인 함수 여야하며 두 번째 인수는 작동 할 데이터입니다. 다음은 명명 된 함수와 동일한 코드입니다.

getQ h
      | pred h = getQ (mutate h)
      | otherwise = h

여전히 혼란 스러우면 아마도 계승이 더 쉬운 예가 될 것입니다.

fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 5 -->* 120

평가에 주목하십시오.

fix (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) 3 -->
let x = (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x in x 3 -->
let x = ... in (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 3 -->
let x = ... in (\d -> if d > 0 then d * (x (d-1)) else 1) 3

오, 방금 봤어요? 그것은 x우리 then지점 내부의 기능 이 되었습니다 .

let x = ... in if 3 > 0 then 3 * (x (3 - 1)) else 1) -->
let x = ... in 3 * x 2 -->
let x = ... in 3 * (\recurse d -> if d > 0 then d * (recurse (d-1)) else 1) x 2 -->

위에서 당신은 기억할 필요가 있습니다 x = f x. 따라서 x 2단지 2.

let x = ... in 3 * (\d -> if d > 0 then d * (x (d-1)) else 1) 2 -->

그리고 여기서 멈출 게요!


귀하의 대답은 실제로 fix저에게 이해가되는 것입니다. 내 대답은 당신이 이미 말한 것에 크게 좌우됩니다.
Dan Burton

@Thomas 두 감소 시퀀스가 ​​모두 올바르지 않습니다. :) id x그냥 감소합니다 x(그런 다음 다시 감소합니다 id x). -그런 다음 두 번째 샘플 ( fact)에서 x썽크가 처음 강제 될 때 결과 값이 기억되고 재사용됩니다. 의 재 계산이 (\recurse ...) x일어날 것 아닌 공유 정의 y g = g (y g)하지이 함께 공유fix 정의. - 여기에서 평가판 편집 을했습니다. 사용을 환영합니다. 또는 승인하면 편집 할 수 있습니다.
Will Ness

실제로 fix id가 줄어들 면 프레임 (thunk) 내부 let x = id x in x의 응용 프로그램 값도 강제 적용 되므로로 감소 하고이 루프가 반복됩니다. 그런 것 같습니다. id xletlet x = x in x
Will Ness

옳은. 내 대답은 방정식 추론을 사용하는 것입니다. 평가 순서와 관련된 하스켈 감소를 보여주는 것은 진정한 이득없이 예제를 혼동하는 역할을합니다.
Thomas M. DuBuisson 2014

1
질문은 haskell과 letrec (즉, 공유와 함께 재귀 적 let)로 태그가 지정됩니다. 하스켈에서 fixY 의 구분 은 매우 명확하고 중요합니다. 올바른 순서가 더 짧고, 훨씬 더 명확하고, 쉽게 따라갈 수있을 때 잘못된 감소 순서를 보여줌으로써 어떤 유익이 제공되는지 알 수 없으며 실제로 진행되는 상황을 올바르게 반영합니다.
Will Ness

3

내가 그것을 이해하는 방법, 그것은 당신이 제공하는 것과 동일한 것을 출력하도록 함수의 값을 찾습니다. 문제는 항상 정의되지 않은 (또는 무한 루프, 하스켈에서 정의되지 않은 루프와 무한 루프가 동일 함) 또는 가장 많이 정의되지 않은 항목을 선택한다는 것입니다. 예를 들어, id를 사용하면

λ <*Main Data.Function>: id undefined
*** Exception: Prelude.undefined

보시다시피 undefined는 고정 된 점이므로이를 fix선택합니다. 대신에 (\ x-> 1 : x).

λ <*Main Data.Function>: undefined
*** Exception: Prelude.undefined
λ <*Main Data.Function>: (\x->1:x) undefined
[1*** Exception: Prelude.undefined

따라서 fix정의되지 않은 것을 선택할 수 없습니다. 무한 루프에 조금 더 연결되도록 만듭니다.

λ <*Main Data.Function>: let y=y in y
^CInterrupted.
λ <*Main Data.Function>: (\x->1:x) (let y=y in y)
[1^CInterrupted.

다시 말하지만 약간의 차이가 있습니다. 그렇다면 고정 소수점은 무엇입니까? 시도해 봅시다 repeat 1.

λ <*Main Data.Function>: repeat 1
[1,1,1,1,1,1, and so on
λ <*Main Data.Function>: (\x->1:x) $ repeat 1
[1,1,1,1,1,1, and so on

동일합니다! 이것이 유일한 고정 점이기 때문에 fix그것을 정해야합니다. 죄송합니다 fix. 무한 루프가 없거나 정의되지 않았습니다.

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