Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about evaluation of lazy sequences

I am experimenting with clojure's lazy sequences. In order to see when the evaluation of an item would occur, I created a function called square that prints the result before returning it. I then apply this function to a vector using map.

(defn square [x]
  (let [result (*  x x)]
  (println "printing " result)
  result))

(def s (map square [1 2 3 4 5])) ; outputs nothing

Here in my declaration of s, the REPL does not output anything. This signals that the computation has not started yet. This appears to be correct. I then do:

(first s)

The function "first" takes only the first item. So I expect that only 1 will be evaluated. My expectation is the REPL will output the following:

printing 1
1

However, the REPL outputted the following instead.

printing 1
printing 4
printing 9
printing 16
printing 25
1

So rather than evaluating only the first item, it seems it evaluates all items, even though I am accessing just the first item.

If the state of a lazy sequence can only be either all values computed and no values computed, then how can it gain the advantages of lazy evaluation? I came from a scheme background and I was expecting more like the behavior of streams. Looks like I am mistaken. Can anyone explain what is going on?

like image 360
lightning_missile Avatar asked Oct 20 '21 15:10

lightning_missile


People also ask

What is the purpose of lazy evaluation?

Lazy evaluation is used in Unix map functions to improve their performance by loading only required pages from the disk. No memory will be allocated for the remaining pages.

Is lazy evaluation strict or non strict?

Lazy evaluation is a form of non-strict evaluation in which arguments are not evaluated until required. A computation that does not terminate under strict evaluation, can terminate under lazy evaluation.

What is lazy evaluation method?

In programming language theory, lazy evaluation, or call-by-need, is an evaluation strategy which delays the evaluation of an expression until its value is needed (non-strict evaluation) and which also avoids repeated evaluations (sharing).

What is a lazy sequence?

Lazy sequences are regular sequences where each item is computed on demand rather than up front. For example, consider this array of numbers: let numbers = Array(1... 100000) That will hold 100,000 numbers.

What is lazy evaluation?

What is Lazy Evaluation? — Programming Word of the Day When a compiler encounters an expression, usually it tries to evaluate it. Evaluation is the process of getting the root meaning of a piece of code. The meaning of 5 + 3 is 8.

What is the difference between strict and lazy evaluation?

The style of only evaluating what's needed is called lazy evaluation, while the opposite (evaluating immediately) is called strict evaluation. It’s called “lazy” because the compiler will procrastinate. It will not be a good student and study in advance.

What does it mean to fully evaluate a sequence?

This means if we call a function sequence defined as which creates an array of integers between l and u and then maps each number to its square our language will fully evaluate sequence (1, 5) before then mapping each value in that array. We are then left with an array squares that has been fully evaluated before we move on the next line of code.

Is a lazy compiler more efficient?

Just like lazy students, a lazy compiler can be much more efficient if it makes smart choices. For lazy evaluation to be efficient, it needs to use memoization. In other words, it needs to keep a dictionary where the key is the name of the variable, and the value is the result of the evaluation.


1 Answers

Laziness isn't all-or-nothing, but some implementations of seq operate on 'chunks' of the input sequence (see here for an explanation). This is the case for vector which you can test for with chunked-seq?:

(chunked-seq? (seq [1 2 3 4 5]))

When given a collection map checks to see if the underlying sequence is chunked, and if so evaluates the result on a per-chunk basis instead of an element at a time.

The chunk size is usually 32 so you can see this behaviour by comparing the result of

(first (map square (vec (range 35))))

This should only display a message for the first 32 items and not the entire sequence.

like image 102
Lee Avatar answered Oct 24 '22 10:10

Lee