5.4 A la Haskell - naver/lispe GitHub Wiki

Functions inspired by Haskell

back

Important: The methods: map, filter, take, repeat, cycle, takewhile, drop, dropwhile, fold, scan are automatically composed one with the others.


(map '+ (map '* '(1 2 3))) 
; is actually transformed into one single loop,
; which takes the following form:
(setq #recipient1 ()) 
(loop #i1 (quote (1 2 3))
      (setq #accu1 (+ (* #i1 #i1) (* #i1 #i1)))
      (push #recipient1 #accu1)
)

Hence, when a sequence of calls to these methods is made, the system automatically factorizes them into one single loop.

 Hence, thanks to that implementation, it is possible to perform lazy evaluations

Note On Composition

If you want to know how your different functions have been composed, the easiest way is to store them in a function and to display the content of that function.


(defun tst(x) (map '* (map '+ x)))

; Displaying the content of 'tst'
(prettify tst)

(defun tst (x)
   (block
      (setq %content0 ())
      (loop %i0 x
         (setq %v0 (* (+ %i0 %i0) (+ %i0 %i0)))
         (push %content0 %v0)
      )
      %content0
   )
)

for: (for x list action)

Applies action to each element from list and yields a list out of the results.

(for i '(1 2 3 4) (* i i)) ; (1 4 9 16)

map: (map op list)

Applies an operation to each item in a list


(map '+ '(1 2 3)) returns (2 4 6)
(map '(+ 1) '(1 2 3)) returns (2 3 4)

; important, we interpret (1 -) as (- x 1)
(map '(1 -) '(1 2 3)) returns (0 1 2)

; Important, we interpret (- 1) as (- 1 x)
(map '(- 1) '(1 2 3)) returns (0 -1 -2)
 
(map (lambda (x) (+ x 1)) '(1 2 3)) returns (2 3 4)

;An amusing example, we execute a shell command
!v=ls

; But 'v' contains strings ending in carriage return.
; This is how you can initialize all the strings at once.

(setq v (map 'trim v))

filter: (filter condition list)

Filters the items in a list. The condition can be a lambda.

(filter '(< 10) '(1 4 5 10 11 20)) returns (1 4 5)
(filter (lambda (x) (< x 10)) '(1 4 5 10 11 20)) returns (1 4 5)

drop: (drop nb list)

Drops nb elements from a list and returns the remainder.

dropwhile: (dropwhile condition list)

Skips all items meeting the condition, then returns the rest.

(dropwhile '( < 10) '(1 3 5 9 10 3 4 12)) returns (10 3 4 12)

take: (take nb list)

Take nb elements from a list.

replicate: (replicate nb value)

Create a list, in which value is repeated nb times.

(replicate 4 '(1 2)) ; yields ((1 2) (1 2) (1 2) (1 2))

repeat: (repeat value)

Create a list, in which value is stored over and over again. It should be associated with a take for instance.

(take 10 (repeat 5)) ; yields: (5 5 5 5 5 5 5 5 5 5)

cycle: (cycle list)

Create a list, in which we cycle through liste to store in. It should be associated with a take for instance.

(take 10 (cycle '(1 2 3)) ; yields: (1 2 3 1 2 3 1 2 3 1)

takewhile: (takewhile condition list)

Keeps all the elements satisfying the condition and then removes the rest.

(takewhile '( < 10) '(1 3 5 9 10 3 4 12))) ; returns (1 3 5 9)

irange: (irange initial increment)

Infinite range, starting at initial by increment step.

(takewhile '(< 10) (irange 1 2)) ; yields: (1 3 5 7 9)

(takewhile '(< 100) (map '* (irange 1 2))) ; yields: (1 9 25 49 81)

(irange initial bound increment)

This specific irange is used to avoid creating a list of integers beforehand in a loop with range. It implements an increment-based loop.


(loop i (irange 0 100 1)
   (println i)
)

(irangein initial bound increment)

This specific irangein is used to avoid creating a list of integers beforehand in a loop with range. It implements an increment-based loop. The difference with irange is that the bound is part of the values.


(loop i (irangein 0 5 1)
   (println i)
)

;0
;1
;2
;3
;4
;5

foldl: (foldl op initial list)

applies an operation on a list, providing an initial value from the beginning of the list

(foldl '- 10 '(1 2 3)) ; gives 4

foldl1: (foldl1 op list)

Same as foldl but takes the first item in the list as first value

(foldl1 '- '(1 2 3)) ; gives -4

foldr: (foldr op initial list)

as foldl but starts from the end of the list

foldr1: (foldr1 op list)

as foldl1 but starts from the end of the list

scanl: (scanl op initial list)

Keeps the list of intermediate items in a list

(scanl '- 10 '(20 30 40)) ; gives (10 -10 -40 -80)

scanl1: (scanl1 op list)

Same thing, but we use the first element of the list for the calculation.

(scanl1 '- '(20 30 40)) ; gives (20 -10 -50)

scanr: (scanr op initial list)

We start from the end of the list for the accumulation

(scanr '+ 0 '(3 5 2 1)) ; gives (11 8 3 1 0)

scanr1: (scanr1 op list)

We start from the end of the list for the accumulation and use the last item for the operations

zip: (zip l1 l2 l3...)

Allows to make a list from the list elements given as arguments.

(zip '(1 2 3) '(4 5 6) '(7 8 9)) ; gives ((1 4 7) (2 5 8) (3 6 9))

zipwith: (zipwith op l1 l2 l3...)

Allows to apply an operator between list items.

(zipwith '+ '(1 2 3) '(4 5 6) '(7 8 9)) ; yields (12 15 18)

zipwith creates a list based on the type of the first element that is returned by the lambda or the operation. For instance, in the above example, zipwith will return a list of integers: integers.

zipwith can take a last parameter, which can be true or false to force the output to be a regular list:

(type (zipwith '+ '(1 2 3) '(4 5 6) '(7 8 9))) ; yields integers_
(type (zipwith '+ '(1 2 3) '(4 5 6) '(7 8 9) false)) ; yields list_

Non Composition Operator: an example

The non composition operator: !prevents LispE from combining two structures:

; We let LispE compose the following expression.
; At each step it processes both the scanl1 and the map
(map '* (scanl1 '+ '(10 20 30))) ; result is (10 900 864900)

; We prevent LispE from composing in the following expression:
(!map '* (scanl1 '+ '(10 20 30))) ; result is (100 900 3600)

Lambdas With: scan/fold

There are two different sorts of scan/fold functions:

  • from the left (indicated with an l at the end of the function name)
  • from the right (indicated with an r at the end of the function name)

These two sorts of function not only process lists in a reverse order, they also compute their values in a reverse order.

Compare for instance:


(foldl1 '- '(1 2 3)) ; yields -4

(foldr1 '- '(1 2 3)) ; yields 2

If you use a lambda function then this lambda must take two arguments, but the order of the arguments depends on the type of functions.

  • left function, the accumulator is the first argument
  • right function, the accumulator is the second argument
; left function, the accumulator is the first argument
(scanl1 (lambda (accu x) (+ accu x 1)) '(1 2 3))

; right function, the accumulator is the second argument
(scanr1 (lambda (x accu) (+ accu x 1)) '(1 2 3))