재귀와 코어 사이의 차이점은 무엇입니까?


55

이것들의 차이점은 무엇입니까?

Wikipedia에는 ​​정보가 거의 없으며 이러한 용어를 설명하는 명확한 코드가 없습니다.

이 용어들을 설명하는 아주 간단한 예는 무엇입니까?

corecursion은 재귀의 이중성에 어떤 영향을 미칩니 까?

고전적인 핵심 알고리즘이 있습니까?


45
SO stackoverflow.com/questions/10138735/…에 대한 답변을 참조하십시오 (죄송합니다, 제 자신을 막을 수 없었습니다)
High Performance Mark

7
@HighPerformanceMark, 그것은 핵심 curcursion이 무엇인지 설명하지 않습니다, 우리는 또 다른 질문이 필요합니다
Abyx

5
그러나 진지하게, 이러한 용어에 대한 Wikipedia의 설명에 어떤 문제가 있습니까?
고성능 마크

5
위키 백과에 대한 핵심 설명은 끔찍합니다. 핵심 소닉이 무엇인지 아직 모르는 사람에게는 이치에 맞지 않습니다.
Marcin

9
@ 고성능 마크 : 나는 펑을 이해하기 전에 실수가 있다고 생각한 링크를 세 번 클릭했습니다. LOL
조르지오

답변:


24

이것을 보는 좋은 방법이 많이 있습니다. 나에게 가장 쉬운 것은 "유도"와 "유도 성 정의"의 관계에 대해 생각하는 것입니다

집합의 귀납적 정의는 다음과 같습니다.

"Nat"세트는 "Zero"가 Nat에 있고 n이 Nat에 있으면 "Succ n"은 Nat에있는 가장 작은 세트로 정의됩니다.

다음에 해당하는 Ocaml

type nat = Zero | Succ of nat

이 정의에 대해 주목해야 할 것은

omega = Succ(omega)

이 세트의 멤버가 아닙니다. 왜? 이제는 오메가가 없다는 것을 제외하고 Nat와 동일한 요소를 갖는 세트 N을 고려하십시오. 분명히 0은 N에 있고 y가 N에 있으면 Succ (y)는 N에 있지만 N은 Nat보다 작기 때문에 모순입니다. 따라서 오메가는 Nat에 없습니다.

또는 컴퓨터 과학자에게 더 유용 할 수 있습니다.

"a"세트가있는 경우, "List of a"세트는 "Nil"이 a의 목록에 있고 xs가 a의 목록에 있고 x가 "Cons x xs"에 있도록 가장 작은 세트로 정의됩니다. 의 목록에 있습니다.

어떤 것에 해당하는

type 'a list = Nil | Cons of 'a * 'a list

여기에 사용 된 단어는 "가장 작습니다". 만약 우리가 "가장 작다"고 말하지 않았다면, 세트 Nat가 바나나를 포함하고 있는지 알 수있는 방법이 없을 것입니다!

다시,

zeros = Cons(Zero,zeros)

omega가 유효한 Nat가 아닌 것처럼 nats 목록에 대한 올바른 정의가 아닙니다.

정의 데이터를 이 사용하여 작업 기능을 정의 할 수있게 해준다처럼 유도 재귀

let rec plus a b = match a with
                   | Zero    -> b
                   | Succ(c) -> let r = plus c b in Succ(r)

그런 다음 유도 (특히 구조적 유도)를 사용하여 "plus a zero = a"와 같은 사실을 증명할 수 있습니다.

우리의 증거는 a.
기본 사례의 경우 a를 0으로 설정하십시오. plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)그래서 우리는 알고 plus Zero Zero = Zero있습니다. anat 하자 . 귀납적 가설을 가정하자 plus a Zero = a. 우리는 지금 보여 plus (Succ(a)) Zero = Succ(a)이 때문에 분명 plus (Succ(a)) Zero = match a with |Zero -> Zero | Succ(a) -> let r = plus a Zero in Succ(r) = let r = a in Succ(r) = Succ(a) 유도에 의해, 따라서 plus a Zero = a모든 aNAT에서

물론 우리는 더 흥미로운 것을 증명할 수 있지만 이것이 일반적인 아이디어입니다.

지금까지 우리는 "가장 작은"집합으로함으로써 얻은 귀납적으로 정의 된 데이터 를 다루었 다 . 이제 공동으로 정의 된 코 데이터 로 작업하고 싶습니다 .

그래서

세트를하자. "a의 스트림"세트는 a의 스트림에서 각각의 x에 대해, x가 헤드가 a이고 꼬리가 a의 스트림에 있도록 순서 쌍 (head, tail)으로 구성되도록 가장 큰 세트로 정의된다

Haskell에서는 이것을 다음과 같이 표현합니다.

data Stream a = Stream a (Stream a) --"data" not "newtype"

실제로 Haskell에서는 기본 제공 목록을 사용합니다.이 목록은 순서가있는 쌍이거나 빈 목록 일 수 있습니다.

data [a] = [] | a:[a]

바나나는 주문 된 쌍이 아니거나 빈 목록이 아니기 때문에이 유형의 구성원이 아닙니다. 그러나 이제 우리는 말할 수 있습니다

ones = 1:ones

그리고 이것은 완벽하게 유효한 정의입니다. 또한이 공동 데이터에 대해 공동 재귀를 수행 할 수 있습니다. 실제로 함수가 동시 재귀 및 재귀가 될 수 있습니다. 재귀는 데이터로 구성된 도메인을 가진 함수에 의해 정의되었지만 동시 재귀는 공동 데이터 인 코 도메인 (범위라고도 함)을 의미합니다. 원시 재귀는 가장 작은 데이터에 도달 할 때까지 작은 데이터 에서 항상 "자신을 호출"하는 것을 의미했습니다 . 원시적 재귀는 항상 이전의 데이터보다 크거나 같은 데이터에 대해 "자신을 호출합니다".

ones = 1:ones

기본적으로 동시 재귀입니다. 함수 map(필수 언어에서 "foreach"와 같은 종류)는 기본적으로 재귀 적 (정렬)과 기본적으로 동시 재귀 적입니다.

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

zipWith함수와 목록 쌍을 가져 와서 그 함수를 사용하여 결합하는 함수도 마찬가지 입니다.

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = (f a b):zipWith f as bs
zipWith _ _ _           = [] --base case

기능적 언어의 고전적인 예는 피보나치 수열입니다

fib 0 = 0
fib 1 = 1
fib n = (fib (n-1)) + (fib (n-2))

기본적으로 재귀 적이지만 무한 목록으로보다 우아하게 표현할 수 있습니다.

fibs = 0:1:zipWith (+) fibs (tail fibs)
fib' n = fibs !! n --the !! is haskell syntax for index at

유도 / 코인 유도의 흥미로운 예는이 두 정의가 동일한 것을 계산한다는 것을 증명하는 것입니다. 이것은 독자를위한 연습으로 남습니다.


1
감사합니다. 코드 일부를 Java로 번역 할 계획입니다. 계속 지켜봐 주시기 바랍니다
Philip JF

@PhilipJF : 어리석은 느낌이 들지만 왜 "... 명확하게 0이 N에 있고 y가 N에 있으면 Succ (y)는 N에 있습니다 ..." y가 Succ (y) = 오메가를 만족시키는 것이라면 어떻게됩니까? (0과 Succ의 속성을 사용하지 않기 때문에 Succ = root square 및 Zero = 2를 대체 할 수 있습니다)
Ta Thanh Dinh

... 그리고 나는 오메가 = 1을 본다.
Ta Thanh Dinh

목표는 오메가가 nat에 없다는 것을 보여주는 것입니다. 우리는 모순으로 이것을합니다. 오메가가 세트보다 nat에 있으면 N = nat-{omega}는 법을 충족합니다. nat가 법을 충족시키기 때문입니다. y가 N이면 1. y는 오메가가 아니며 2. y는 nat입니다. 2에서 우리는 nat에서 Succ (y)를 알고 있으며, 1y는 오메가가 아닙니다. Succ (y)는 오메가가 아닙니다. 따라서 N의 Succ (y)는 N도 포함합니다. 그러나 N은 nat보다 작습니다. 이것은 모순입니다. 따라서 nat는 오메가를 포함하지 않습니다.
Philip JF

ocaml은 가치 재귀를 가지고 있기 때문에 이것은 약간의 거짓말입니다. 실제로 유도 성 추론을 지원하는 유일한 "주류"언어 인 SML을 사용해야했습니다.
Philip JF

10

기본적으로 corecursion은 재귀 누산 자 스타일로 시작 사례에서 앞으로 나아갈 때 결과를 작성하는 반면 일반 재귀는 기본 사례에서 돌아 오는 길에서 결과를 만듭니다.

(지금 Haskell 말하기). 이유 foldr(엄격한 조합 기능)이 재귀를 표현하고, foldl'(엄격한 빗. F.) / scanl/ until/ iterate/ unfoldr/ corecursion 등을 표현한다. Corecursion은 어디에나 있습니다. foldr엄격하지 않은 빗. 에프. 꼬리 재귀 모듈로 단점을 표현합니다 .

그리고 Haskell의 보호 된 재귀 꼬리 재귀 모듈로 단점 과 같습니다 .

이것은 재귀입니다.

fib n | n==0 = 0
      | n==1 = 1
      | n>1  = fib (n-1) + fib (n-2)

fib n = snd $ g n
  where
    g n | n==0 = (1,0)
        | n>0  = let { (b,a) = g (n-1) } in (b+a,b)

fib n = snd $ foldr (\_ (b,a) -> (b+a,b)) (1,0) [n,n-1..1]

( $"of"로 읽음 ). 이것이 핵심 cursion입니다.

fib n = g (0,1) 0 n where
  g n (a,b) i | i==n      = a 
              | otherwise = g n (b,a+b) (i+1)

fib n = fst.snd $ until ((==n).fst) (\(i,(a,b)) -> (i+1,(b,a+b))) (0,(0,1))
      = fst $ foldl (\(a,b) _ -> (b,a+b)) (0,1) [1..n]
      = fst $ last $ scanl (\(a,b) _ -> (b,a+b)) (0,1) [1..n]
      = fst (fibs!!n)  where  fibs = scanl (\(a,b) _ -> (b,a+b)) (0,1) [1..]
      = fst (fibs!!n)  where  fibs = iterate (\(a,b) -> (b,a+b)) (0,1)
      = (fibs!!n)  where  fibs = unfoldr (\(a,b) -> Just (a, (b,a+b))) (0,1)
      = (fibs!!n)  where  fibs = 0:1:map (\(a,b)->a+b) (zip fibs $ tail fibs)
      = (fibs!!n)  where  fibs = 0:1:zipWith (+) fibs (tail fibs)
      = (fibs!!n)  where  fibs = 0:scanl (+) 1 fibs
      = .....

접기 : http://en.wikipedia.org/wiki/Fold_(higher-order_function)


4

Vitomir Kovanovic의 블로그 에서 확인하십시오 . 나는 요점을 발견했다.

lisp, haskell, python 등과 같은 기능적 프로그래밍 기능이있는 프로그래밍 언어에서 볼 수있는 매우 훌륭한 기능 중 하나 인 게으른 평가. 변수 값의 평가는 해당 변수의 실제 사용으로 지연됩니다.

예를 들어 이와 같은 것을 사용하여 백만 개의 요소 목록을 만들려면 (defn x (range 1000000))실제로 생성되지는 않지만 실제로 지정되지 않고 처음으로 해당 변수를 사용할 때, 예를 들어 10 번째 요소를 원할 때 해당 목록 해석기는 해당 목록의 처음 10 개 요소 만 작성합니다. 따라서 첫 번째 실행 (10 x 테이크)은 실제로 이러한 요소를 생성하며 동일한 함수에 대한 모든 후속 호출은 이미 존재하는 요소로 작동합니다.

메모리 부족 오류없이 무한 목록을 만들 수 있기 때문에 매우 유용합니다. 요청한 양만큼 목록이 커집니다. 물론 프로그램이 큰 데이터 수집 작업을하는 경우 이러한 무한 목록 사용시 메모리 제한에 도달 할 수 있습니다.

반면에 코어 커런 은 재귀와 관련이 없습니다. 이게 무슨 뜻이야? 자체 용어로 표현되는 재귀 함수와 마찬가지로, 코어 재귀 변수는 자체 용어로 표현됩니다.

이것은 예제에서 가장 잘 표현됩니다.

모든 소수 목록을 원한다고 가정 해 봅시다 ...


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