2

나는 과거 하스켈과 도박을 해왔고, 최근에 심각하게 그걸로 돌아 왔고, 나는 실제 세계의 하스켈을 읽고있다. 그들이 빛난 몇 가지 예는 아직 이해하지 못했습니다. 다음과 같이 :누군가 나에게 하스켈 함수를 설명 할 수 있을까요?

myLength []  = 0 
myLength (x:xs) = 1 + myLength (xs) 

이 작동 방식이 보이지 않습니다. 1이 실제로 추가되는 이유는 무엇입니까? 재귀는 어떻게 추가 할 수있는 어떤 것을 반환합니까? 나는 그것을 얻지 않는다.

그리고 여기에 우리는이 하나 있습니다

splitLines [] = [] 
splitLines cs = 
     let (pre, suf) = break isLineTerminator cs 
     in pre : case suf of 
        ('\r':'\n':rest) -> splitLines rest 
        ('\r':rest)  -> splitLines rest 
        ('\n':rest)  -> splitLines rest 
        _    -> [] 

isLineTerminator c = c == '\r' || c == '\n' 

어떻게이 작품은, 정말 너무 무엇을 부착하고 사전입니다 않습니다를? 나는 사건 표현의 결과가 어떻게 pre가 연결될 수 있는지를 보지 못한다. 어쩌면이 함수의 평가를 자세히 설명하는 누군가가 필요할 수도 있습니다. 나는 아주 중요한 것을 놓치고 있어야합니다.

미리 감사드립니다.

EDIT : 복사 붙여 넣기가 실패했습니다. 죄송합니다.

편집 2 : 내 혼란은 이러한 기능이 실제로/돌아 왔는지와 관련이있는 것처럼 보이지만 지금은 모두 해결되었습니다. 대답 녀석 주셔서 감사합니다, 그것은 결국 클릭! 감사합니다! 이 정의와

myLength [] = 0 
myLength (x:xs) = 1 + myLength (xs) 

, 빈리스트의 myLength(x:xs) 귀갑은 첫 번째 항목으로 목록을 압축을 풉니 다 0입니다 :

답변

10

첫 번째는 매우 기본적인 재귀 방식입니다. 그러나, 일부 누락 된 것으로 보인다

myLength [] = 0 

그것은 목록에서 한 번에 하나의 요소를 확장하고 그 결과에 1을 추가하여 사용할 수 있습니다. 고려로 평가됩니다 호출

myLength [1,2,3] 

시각화 : 두 번째에 관해서는 3

입니다

1 + myLength [2,3] 
1 + 1 + myLength [3] 
1 + 1 + 1 + myLength [] 
1 + 1 + 1 + 0 

을, 잘, 당신은 이미 다음에 문자열을 분할했다 두 부분으로 줄 바꿈 : pre와 suf. 이제 suf는 \ n 또는 \ r 또는 \ r \ n으로 시작합니다. 우리는 이것을 제거하고 싶다. 그래서 우리는 패턴 매칭을 사용합니다. 나머지 변수가 근본적으로 suf 변수에서 처음 줄 바꿈 문자를 뺀 값인 방법을보십시오.

그래서 첫 줄에는 pre가 있고 나머지 줄에는 나머지가 있습니다. 따라서 나머지를 줄로 나누기 위해 splitLines를 반복적으로 호출하고 그 결과를 pre에 연결합니다.

시각화하려면 "foo \ nbar \ r \ nbaz"라는 문자열이 있다고 가정 해보십시오.호출 할 때

그래서, 결과는 다음과 같습니다

[ pre => foo, suf => \nbar\r\nbaz, rest => bar\r\n\baz ] 
foo : splitLines bar\r\nbaz 

다음 splitLines가 다시 호출하고, 결과가로 확장되어 다시 한번

[ pre => bar, suf => \r\nbaz, rest = baz ] 
foo : bar : splitLines baz 

:

[ pre => baz, suf => [], rest = [] ] 
foo : bar : baz 

최종 결과입니다.

+0

나는 이것이 어떻게 작동 하는지를 이해하지만, 지금 이것이 왜 효과가 있는지에 관해서는 분실했다. 결과는 다른 재귀 호출이거나 빈 목록입니다. 본질적으로, 그것은 pre가 모든 반복되기 전에 연결되는 것처럼 보이지만, case 표현은 pre를 반환하지 않습니다. 혼란스러워. – Rayne

+0

실제로 다시 읽은 후, pre가 결과가 아닌 splitLines의 인수에 연결되는 것처럼 보입니다. 나는 이것이 []를 반환 할 재귀 호출 이상의 것을 반환하는 방법을 모르겠다. 나는 내가 생각한만큼 재귀를 이해하지 못한다고 생각한다. \ – Rayne

+0

함수를 보면, case 표현식은 결과 인 재귀 호출의 결과 인 재귀 호출의 결과를 반환하는 것처럼 보인다. 재귀 호출의 [], []를 반환 할 때까지 계속됩니다. 나는이 상황에서 휴식이 연계되어있는 것을 보지 못한다. – Rayne

4

나는 myLength의 정의 목록이 비어있는 경우 그리워 생각 a 및 나머지 항목 목록은 xs입니다. 목록에 항목이 하나있는 경우 xs은 빈 목록이므로 결과는 1 + 0입니다.

기본 사례를 먼저 살펴본 다음 재귀의 각 수준이 결과에 어떻게 빌드되는지 볼 때 재귀를 가장 쉽게 이해할 수 있습니다. (기본 경우는 함수 자체를 호출하지 않는 경우입니다. 재귀 함수에 기본 사례가 없으면 출력은 무한대가됩니다.)

두 번째 예에서 기본 사례 case-statment의) 또한 빈 목록입니다. 그래서 pre는 항상 목록에 추가 될 것이며, 이것은 더 길고 새로운 목록을 만들어 낼 것입니다.

2

재 : myLength (x:xs) = 1 + myLength (xs) -이 경우 인수가 머리와 꼬리, 결과는 재귀 꼬리보다 1 인을 가지고, 패턴 일치에 의해, 그것은 말한다 myLength의 정의 "반"입니다 꼬리를 호출 - 인자가 x:xs과 일치하지 않을 때, 즉 인수가 빈 목록 인 경우 결과가 0이라고 말할 수있는 절반이 필요합니다.

두 번째 경우에는 다른 패턴 일치 가능성이 case을 통해 조금 더 분명해질 수 있습니다.

여기에 게으름은 중요한 문제가 아닙니다. ML은 열심히 평가하지만 하스켈처럼 패턴 일치가 매우 비슷하게 작동합니다. 패턴 일치는 실제로 브러쉬해야 할 필요가있는 것처럼 보입니다.(: 당신이 지금 그것을 고정 된 것처럼 보이는 편집) : 모든 첫 번째 예제의

+0

답변 해 주셔서 감사합니다. 패킹 된 패턴 매칭을 가지고 있고, 재귀에서 꽤 잘하고 있습니다. 내 실수는이 기능들이 실제로/돌아왔다 /는 것입니다. – Rayne

2

먼저 다음과 같이해야한다

그것은 다음과 같이 작동
myLength []  = 0 
myLength (x:xs) = 1 + myLength (xs) 

는 : 나는 세 가지 항목으로 그것을 목록을 제공 말한다 1을 더한 꼬리의 길이에 1을 더한 값 (꼬리의 길이에 1을 더한 것)과 꼬리의 길이를 더한 것 (이 시점에서 []입니다.)은 1)입니다. 3 (최종 ​​답)입니다. 어쩌면 중첩 된 괄호로 이해하는 데 도움이됩니다. ;-)

1

함수의 형식 시그니처가 무엇인지 살펴 보는 것이 좋습니다. 그들은 같을 수

myLength :: [a] -> Int 

myLength에서, (1)는 IntmyLength에 재귀 호출의 결과에 부가되고있는 Int 차례로 결과이다.

splitLines :: [Char] -> [[Char]] 

splitLines에서 pre은 (a [Char])는 케이스보고에서, 하나 [[Char]]이다 splitLines 재귀 호출의 결과, 케이스 명세서의 결과로 미리 결정되고; 또는 빈 목록. 두 경우 모두 앞에 pre ([Char])을 붙이면 차례로 [[Char]]이됩니다.

+1

좋아요, myLength 예제를 이해합니다. 그러나이 책에서 : (pre :)의 두 번째 인수는 case 표현식의 결과이지만 case 표현식의 결과는 비어있는 목록 (나) 또는 splitLines에 대한 재귀 호출 중 하나입니다. 사전에 무엇인가 추가 되었습니까? 나는 이것을 내 마음 속에서 해결하려고 노력하고있다. 바보 인 것에 대해 유감스럽게 생각합니다. : \ – Rayne

+0

더, 내가 묻는 것은 splitLines에 대한 재귀 호출이 어떻게 [[Char]]가 되는가하는 것입니다. – Rayne

+0

글쎄, (:)은 "죄수"연산자라는 것을 알고 있습니다. 그것은 요소와 목록을 취하여 그 요소가 다른 목록 앞에 붙은 목록을 반환합니다. 그래서 (:)에는 "a -> [a] -> [a]"유형이 있습니다. 그래서 pre는 유형 [Char]을 가지고 있으므로 splitLines의 결과는 [[Char]] 유형입니다. – newacct

관련 문제