이것은 당신이 배우고 자하는 프로그래밍 언어에 더 유창하게되기위한 좋은 연습입니다. 여기에는 객체 작업, 클로저 사용 또는 시뮬레이션, 유형 시스템 확장이 포함됩니다.
당신의 임무는 게으른 목록을 관리하는 코드를 작성하고 피보나치 수를 생성하기 위해이 알고리즘을 구현하는 데 사용하는 것입니다.
코드 샘플은 Haskell에 있습니다
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
결과:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
지연 목록 구현은 다음 지침을 충족해야합니다.
- 목록 노드는 다음 세 가지 중 하나입니다.
- 무기명-빈 목록.
[]
- 단점-나머지 항목의 목록과 쌍을 이루는 단일 항목 :
1 : [2,3,4,5]
(:
Haskell의 단점 연산자 임) - 썽크-필요할 때 목록 노드를 생성하는 지연된 계산.
- 무기명-빈 목록.
- 다음 작업을 지원합니다.
- nil-빈리스트를 작성합니다.
- cons-죄수 셀을 구성합니다.
- thunk-인수를 취하지 않고 Nil 또는 Cons를 리턴하는 함수가 주어지면 Thunk를 구성하십시오.
- force-주어진 목록 노드 :
- Nil 또는 Cons 인 경우 간단히 반품하십시오.
- Thunk 인 경우 함수를 호출하여 Nil 또는 Cons를 얻습니다. 썽크를 해당 Nil 또는 Cons로 교체하고 반환하십시오.
참고 : 썽크를 강제 값으로 바꾸는 것은 "lazy"정의의 중요한 부분입니다 . 이 단계를 건너 뛰면 위의 피보나치 알고리즘이 너무 느려집니다.
- empty-List 노드가 Nil인지 확인합니다 (강제 한 후).
- head (일명 "car")-목록의 첫 번째 항목을 가져옵니다 (또는 Nil 인 경우 맞추기).
- tail (일명 "cdr")-목록의 머리 다음에 요소를 가져 오거나 Nil 인 경우 맞춤을 던집니다.
- zipWith-이진 함수 (예 :)
(+)
와 두 개 (무한대) 목록이 주어지면 해당 함수를 목록의 해당 항목에 적용합니다. 예:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take-숫자 N과 (무한한) 목록이 주어지면 목록의 첫 N 항목을 가져옵니다.
- print-목록에있는 모든 항목을 인쇄합니다. 길거나 무한한 목록이 주어지면 점진적으로 작동합니다.
fibs
자체 정의에서 자체를 사용합니다. 게으른 재귀를 설정하는 것은 약간 까다 롭습니다. 다음과 같은 작업을 수행해야합니다.- 에 썽크를 할당하십시오
fibs
. 지금은 더미 상태로 두십시오. - 에 대한 참조에 따라 썽크 함수를 정의하십시오
fibs
. - 기능으로 썽크를 업데이트하십시오.
fix
자체 반환 값으로 List-returning 함수를 호출하는 함수 를 정의하여이 배관을 숨길 수 있습니다 . 이 아이디어를 설정할 수 있도록 짧은 낮잠을 고려하십시오.- 에 썽크를 할당하십시오
다형성 (모든 유형의 항목 목록으로 작업하는 기능)은 필요하지 않지만 언어에서 관용적 인 방법을 찾을 수 있는지 확인하십시오.
- 메모리 관리에 대해 걱정하지 마십시오. 가비지 콜렉션이있는 언어조차도 다시는 사용하지 않는 객체 (예 : 호출 스택)를 가지고 다니는 경향이 있으므로, 무한 목록을 순회하면서 프로그램이 메모리를 누출하더라도 놀라지 마십시오.
언어의 특정 사항을 수용하거나 게으른 목록에 대한 다른 접근 방식을 탐색하려면이 지침에서 약간 벗어나십시오.
규칙 :
- 잘 모르는 언어를 선택하십시오. 이것을 "필수"할 수 없으므로 "명예 시스템"태그입니다. 그러나 유권자들은 귀하의 기록을 확인하여 귀하가 게시 한 언어를 확인할 수 있습니다.
언어의 내장 지연 목록 지원을 사용하여 모든 것을 수행하지 마십시오. 실질적이거나 흥미로운 것을 게시하십시오.
하스켈은 꽤 나왔습니다. 즉, 다음과 같이하지 않으면 :
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
참고 : Haskell의 엄격하지 않은 평가는 제한이 없지만 지연 목록 구현은 직접 기능을 파생해서는 안됩니다. 실제로 게으름이 필요하지 않은 효율적이고 순수하게 기능적인 솔루션을 보는 것이 흥미로울 것입니다.
파이썬 :
- itertools를 사용하지 마십시오.
- 제너레이터는 훌륭하지만,이를 사용하면 강제 값을 메모 할 방법을 찾아야합니다.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. 그러나 zipWith에 대한 두 인수가 모두 무한한 목록이므로 위의 피보나치 알고리즘에는 문제가되지 않습니다.
fibs
있습니다. 자체 구현에 따라 올바르게 구현하기 위해 특별한 작업을 수행 해야합니다. 게으른 재귀에 대해 자세히 설명하기 위해 질문을 업데이트했습니다. FUZxxl 은 그 / 그녀 / 자신이 알아 냈습니다.
zipWith
길이가 다른 두 목록을 호출 할 때 동작은 무엇입니까 ?