UFO ET IT

Clojure : 휴식 대 다음

ufoet 2020. 11. 17. 21:29
반응형

Clojure : 휴식 대 다음


나는 사이의 차이를 이해하는 힘든 시간을 보내고있어 restnextClojure의의를. 게으름에 대한 공식 사이트의 페이지 는 선호도가 아마도를 사용하는 것이되어야 함을 rest나타내지 만 둘의 차이점을 명확하게 설명하지는 않습니다. 아무도 통찰력을 제공 할 수 있습니까?


링크 한 페이지 next설명했듯이, rest반환할지 nil또는 seq 인지 알기 위해 게으른 사기의 구조를 평가해야하기 때문에 (새로운 동작)보다 엄격 합니다 .

rest반면에 항상 seq를 반환하므로 실제로 결과를 사용할 때까지 평가할 필요가 없습니다 rest. 즉, restnext.


이것 만 있으면 쉽습니다 :

(next '(1))
=> nil

따라서 next다음을 살펴보고 라인이 비어 있으면 nil빈 seq 대신 반환 됩니다. 즉, (반환 할 첫 번째 항목까지) 미리보기가 필요하므로 완전히 게으르지 않습니다 (다음 값은 필요하지 않지만 next미리보기 위해 컴퓨팅 시간을 낭비 함).

(rest '(1))
=> ()

rest 앞을 보지 않고 나머지 seq 만 반환합니다.

아마도 당신은 왜 여기에서 두 가지 다른 것을 사용하는 것을 귀찮게 생각합니까? 그 이유는 일반적으로 seq에 아무것도 남아 있지 않은지 확인하고 그냥 반환하기를 원하기 때문입니다 nil. 그러나 어떤 경우에는 성능이 매우 중요하고 하나 이상의 항목을 평가하는 것은 사용할 수있는 엄청난 노력을 의미 할 수 있습니다 rest.


next입니다 (seq (rest ...)).

rest시퀀스의 나머지 부분을 반환합니다. 시퀀스의 해당 부분이 아직 실현 rest되지 않은 경우 강제로 실행하지 마십시오. 시퀀스에 더 많은 요소가 남아 있는지 여부도 알려주지 않습니다.

next동일한 작업을 수행하지만 시퀀스의 적어도 하나의 요소가 실현되도록 강제합니다. 따라서 nextreturns nil이면 시퀀스에 더 이상 요소가 남아 있지 않음을 알 수 있습니다.


next이스케이프 평가가 더 간단하고 깔끔 하기 때문에 이제는 reursion과 함께 사용하는 것을 선호합니다 .

(loop [lst a-list]
    (when lst
        (recur (next lst))

vs

(loop [lst a-list]
    (when-not (empty? lst)   ;; or (when (seq? lst)
        (recur (rest lst))

그러나을 rest사용하는 경우는 컬렉션을 대기열 또는 스택으로 사용하는 경우입니다. 이 경우 마지막 항목을 팝하거나 대기열에서 제거 할 때 함수가 빈 컬렉션을 반환하도록합니다.


다음은 "mundane"재귀 (스택 사용)를 사용하거나 recur(꼬리 재귀 최적화를 사용하여 실제로 루프를 사용하여 ) 시퀀스를 순회하는 코드를 작성할 때 유용한 작은 테이블 입니다.

휴식, 다음, 먼저, seq, 오 마이!

rest및의 동작 차이에 유의하십시오 next. seq이것 과 결합 하면 다음 관용구로 이어집니다. 여기서 end-of-list는 테스트를 거쳐 seq나머지 목록은 rest( "The Joy of Clojure"에서 발췌)를 통해 얻습니다 .

; "when (seq s)":
; case s nonempty -> truthy -> go
; case s empty    -> nil -> falsy -> skip
; case s nil      -> nil -> falsy -> skip

(defn print-seq [s]
  (when (seq s)          
     (assert (and (not (nil? s)) (empty? s)))
     (prn (first s))     ; would give nil on empty seq
     (recur (rest s))))  ; would give an empty sequence on empty seq

next더 열심 rest입니까?

경우 (next coll)평가 결과가 될 수 있습니다 nil. nil호출자가의 진실성을 기반으로 분기 할 수 있으므로 이는 즉시 알려야합니다 (즉, 실제로 반환되어야 ) nil.

If (rest coll) is evaluated, the result cannot be nil. Unless the caller then tests the result for empty-ness using a function call, generation of a "next element" in a lazy-seq can be delayed to the time it is actually needed.

Example

A completely lazy collection, all the computations are "on hold until needed"

(def x
   (lazy-seq
      (println "first lazy-seq evaluated")
      (cons 1
         (lazy-seq
            (println "second lazy-seq evaluated")
            (cons 2
               (lazy-seq
                  (println "third lazy-seq evaluated")))))))           

;=> #'user/x

The computation of "x" is now suspended at the first "lazy-seq".

Using the eager next after that, we see two evaluations:

(def y (next x))

;=> first lazy-seq evaluated
;=> second lazy-seq evaluated
;=> #'user/y

(type y)

;=> clojure.lang.Cons

(first y)

;=> 2
  • The first lazy-seq is evaluated, leading to the printout first lazy-seq evaluated
  • This results in a nonempty structure: a cons with 1 on the left and a lazy-seq on the right.
  • next may have to return nil if the right branch is empty. So we need to check one level deeper.
  • The second lazy-seq is evaluated, leading to the printout second lazy-seq evaluated
  • This results in a nonempty structure: a cons with 2 on the left and a lazy-seq on the right.
  • So don't return nil, return the cons instead.
  • When obtaining the first of y, there is nothing do do except retrieve 2 from the already-obtained cons.

Using the lazier rest, we see one evaluation (note that you have to redefine x first to make the this work)

(def y (rest x))

;=> first lazy-seq evaluated
;=> #'user/y

(type y)

;=> clojure.lang.LazySeq

(first y)

;=> second lazy-seq evaluated
;=> 2
  • The first lazy-seq is evaluated, leading to the printout first lazy-seq evaluated
  • This results in a nonempty structure: a cons with 1 on the left and a lazy-seq on the right.
  • rest never returns nil, even if the lazy-seq on the right would evaluate to the empty seq.
  • If the caller needs to know more (is the seq empty?), he can perform the appropriate test later on the lazy-seq.
  • So we are done, just return the lazy-seq as result.
  • When obtaining the first of y, the lazy-seq needs to be evaluated one step further to obtain the 2

Sidebar

Note that y's type is LazySeq. This may seem obvious, but LazySeq is not at "thing of the language", it is a "thing of the runtime", representing not a result but a state of computation. In fact (type y) being clojure.lang.LazySeq just means "we don't know the type yet, you have to do more to find out". Whenever a Clojure function like nil? hits something that has type clojure.lang.LazySeq, computation will occur!

P.S.

In Joy of Clojure, 2nd edition, on page 126, there is an example using iterate to illustrate the difference between next and rest.

(doc iterate)
;=> Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free
;   of side-effects

결과적으로이 예제는 작동하지 않습니다. 이 경우 실제로 next사이의 동작에는 차이가 없습니다 rest. 이유가 확실 next하지 않은 경우 nil여기에 반환되지 않고 rest.

(defn print-then-inc [x] (do (print "[" x "]") (inc x)))

(def very-lazy (iterate print-then-inc 1))

(def very-lazy (rest(rest(rest(iterate print-then-inc 1)))))
;=> [ 1 ][ 2 ]#'user/very-lazy
(first very-lazy)
;=> [ 3 ]4

(def less-lazy (next(next(next(iterate print-then-inc 1)))))
;=> [ 1 ][ 2 ]#'user/less-lazy
(first less-lazy)
;=> [ 3 ]4

참고 URL : https://stackoverflow.com/questions/4288476/clojure-rest-vs-next

반응형