2015-02-06 3 views
1

나는 clojure의 지연 시퀀스에 대해 궁금해한다. REPL에서 나는 변수 foo는 정의 : foo는 실행 평가 처음 당시Clojure의 게으름에 관하여

user> (def foo (map println [1 2 3])) 
#'user/foo 

을, 그것을 작동하는 것 같다 :

user> foo 
1 
2 
3 
(nil nil nil) 

을하지만 처음 시간 이후, 왜 게으르다?

user> foo 
(nil nil nil) 

답변

3

printlnfoo부작용 println입니다 평가 당신이 처음에보고있는하는 순수 함수가 아닙니다. 두 번째로 foo을 평가할 때 println의 결과는 (map println [1 2 3])의 결과가 이고이므로 다시 호출되지 않습니다.

그리고 foo을 정의하면 콘솔에 아무 것도 인쇄되지 않기 때문에 map이 게으른 것을 알 수 있습니다. foo을 평가할 때만 무언가가 인쇄됩니다.

Laziness in Clojure을 참조하십시오.

당신은 inc 같은 순수 기능을 사용하는 경우 :

(def foo (map inc [1 2 3])) 

> foo 
(2 3 4) 

> foo 
(2 3 4) 

결과는 항상 부작용없이 동일합니다. Clojure의에서 map, filter, etc순수 기능을 사용하도록 설계되어 있지만, 언어는 부작용와 기능을 사용하여 당신을 금지하지 않습니다. Haskell에서, 예를 들어 상응하는 표현식을 작성할조차 수 없다면, 코드는 컴파일되지 않을 것이다.

+0

따라서 (map println [1 2 3]) 결과는 첫 번째 실행 후에 캐시됩니다. 캐시 된 결과는 시퀀스 자체가 아니지만 seqal 개체입니다. 내 이해가 맞습니까? –

+1

결과는 시퀀스'[1 2 3]'의 모든 요소에'println'을 적용한 결과를 포함하는'seqable '입니다. 하지만'println'은'nil'을 리턴하므로'map'에 의해 반환 된'seqable'은'(nil nil nil)'입니다. –

2

컬렉션에 값이 있습니다. println이 반환하는 값은 nil입니다. println의 부작용은 화면에 무언가를 나타나게하는 것입니다.

매핑하여 생성 된 값 println은 var에 저장됩니다. 이것은 println에 의해 반환 된 nil 값의 lazy-seq입니다.

1

질문에 대해 자세히 설명해주세요. println은 기본값으로 표준 출력에 바인드 된 *out* 스트림에서만 부작용이 있습니다.

map과 같은 기능에서 인쇄 및 일부 값을 반환 할 수 있습니다 (예 : map).

user> (defn print-and-inc [n] 
     (do 
      (println "called with n= " n) 
      (inc n))) 
#'user/print-and-inc 

do 시퀀스의 각 식을 실행하고,이 경우에, 마지막의 (inc n)를 결과를 반환한다. 당신은 vector

user> (def foo (map print-and-inc [1 2 3 4 5])) 
#'user/foo 
user> 
user> foo 
called with n= 1 
called with n= 2 
called with n= 3 
called with n= 4 
called with n= 5 
(2 3 4 5 6) 
user> 
user> foo 
(2 3 4 5 6) 

int의 이상 print-and-inc의 매핑으로 지금 foo 정의하고 인쇄 foo가 호출 처음에만 발생하기 때문에 당신이 map의 lazyness를 참조하십시오. 그러나 이제 foo은 초기 컬렉션의 증가 된 값인 결과를 보유합니다.

참고 :이 코드로/추적 정보를 정기적으로 로그인하는 데 사용할 수 있지만 다른 사람에 의해 지적 된 내용뿐만 아니라, REPL 내 게으름을 실험하는 것은주의 tools.logging

1

표준 라이브러리가있다 조금 문제가있어. 게으른 시퀀스는 값을 사용하는 동작에 의해 실현 될 때까지 실제로 값을 가지지 않습니다. repl에는 결과를 인쇄 할 때 암시 적 doall이 있습니다. 이것은 repl에서 replicate을 사용할 때 시퀀스가 ​​종종 실현된다는 것을 의미하지만 실제 코드에서 사용하면 그렇지 않을 수도 있습니다. 코드를 실행할 때 repl 암시 적 doall이 호출되지 않았기 때문에 시퀀스가 ​​예상되는 시점에 실현되지 않았으므로 예기치 않은 결과가 발생합니다. 이것이 혼란을 야기 할 수있는 방법의 예로서, 다음을보십시오. http://nicksellen.co.uk/2013/10/26/clojure-lazy-repl.html

+0

이 게시물은 새로운 clojure 개발자에게 매우 중요합니다. 고마워요! –