Clojure, concetti fondamentali X

c1

if

if è il solo operatore condizionale in Clojure. La forma è: se la prima espressione è logicamente true allora la seconda espressione è valutata e il suo valore ritornato. Altrimenti il risultato è quello della terza espressione. Sia la seconda che la terza espressione vengono valutate solo se necessario.

Per Clojure è vero tutto quello che non è nil o false:

i0

Come si vede se la condizione è falsa e non c’è l’else il risultato è nil.

Da if derivano numerose istruzioni specializzate:

  • when, utile quando dev’essere ritornato nil se la condizione è false;
  • cond, simile a else if di Java e elif di Python;
  • if-let e when-let, composizione rispettivamente di if e when con let. Se il valore è vero viene collegato a un for locale per l’estensione dell’espressione then.

Notare che i predicati true? e false? non sono collegati a if:

i1

true? e false? verificano per true e false, non per la condizione logica true usata da if che è equivalente a (or (not (nil? x)) (true? x)):

i2

Cicli: loop e recur

Clojure ha diversi cicli, come doseq e dotimes costruiti tutti su recur. recur trasferisce il controllo al loop immediatamente precedente senza consumare lo stack. Esempio (countdown):

i3

che naturalmente può essere scritta molto meglio, così:

i4

(devo ancora prenderci la mano ;-))

Uso di recur: recur è un loop a basso livello e la ricorsione non è necessaria quando:

  • quando possibile usare doseq e dotimes;
  • per iterare su una collezione o sequenza è preferibile usare map, reduce, for

Siccome recur non consuma lo stack (e quindi evita gli errori di stack overflow) è adatto a algoritmi ricorsivi. Inoltre è ottimo per operazioni con molti calcoli.
Poi ci sono casi più complessi in cui usando solo map, reduce e simili diventa difficoltoso o inefficiente. In questi casi recur è fondamentale.

Tanto per fare scena guarda qua:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(ns clojureprogramming.mandelbrot
    (:import java.awt.image.BufferedImage
        (java.awt Color RenderingHints)))

(defn- escape
    "Returns an integer indicating how many iterations were required
     before the value of z (using the components `a` and `b`) could
     be determined to have escaped the Mandelbrot set. If z
     will not escape, -1 is returned."
    [a0 b0 depth]
    (loop [a a0
           b b0
           iteration 0]
    (cond
        (< 4 (+ (* a a) (* b b))) iteration
        (>= iteration depth) -1
        :else (recur (+ a0 (- (* a a) (* b b)))
                     (+ b0 (* 2 (* a b)))
                     (inc iteration)))))

(defn mandelbrot
    "Calculates membership within and number of iterations to escape
     from the Mandelbrot set for the region defined by `rmin`, `rmax`
     `imin` and `imax` (real and imaginary components of z,          respectively).
     Optional kwargs include `:depth` (maximum number of iterations
     to calculate escape of a point from the set), `:height` ('pixel'
     height of the rendering), and `:width` ('pixel' width of the
     rendering).

     Returns a seq of row vectors containing iteration numbers for when
     the corresponding point escaped from the set. -1 indicates points
     that did not escape in fewer than `depth` iterations, i.e. they
     belong to the set. These integers can be used to drive most common
     Mandelbrot set visualizations."
    [rmin rmax imin imax & {:keys [width height depth]
                            :or {width 80 height 40 depth 1000}}]
    (let [rmin (double rmin)
        imin (double imin)
        stride-w (/ (- rmax rmin) width)
        stride-h (/ (- imax imin) height)]
    (loop [x 0
           y (dec height)
           escapes []]
    (if (== x width)
        (if (zero? y)
            (partition width escapes)
            (recur 0 (dec y) escapes))
        (recur (inc x) y (conj escapes (escape (+ rmin (* x stride-w))
                                               (+ imin (* y stride-h))
                                               depth)))))))

    (defn render-text
        "Prints a basic textual rendering of mandelbrot set membership,
         as returned by a call to `mandelbrot`."
        [mandelbrot-grid]
        (doseq [row mandelbrot-grid]
            (doseq [escape-iter row]
                (print (if (neg? escape-iter) \* \space)))
            (println)))

    (defn render-image
        "Given a mandelbrot set membership grid as returned by a call to                                            
         `mandelbrot`, returns a BufferedImage with the same resolution as the
         grid that uses a discrete grayscale color palette."
        [mandelbrot-grid]
        (let [palette (vec (for [c (range 500)]
                (Color/getHSBColor 0.0 0.0 (/ (Math/log c) (Math/log 500)))))
            height (count mandelbrot-grid)
            width (count (first mandelbrot-grid))
            img (BufferedImage. width height BufferedImage/TYPE_INT_RGB)
            ^java.awt.Graphics2D g (.getGraphics img)]
            (doseq [[y row] (map-indexed vector mandelbrot-grid)
                   [x escape-iter] (map-indexed vector row)]
                (.setColor g (if (neg? escape-iter)
                (palette 0)
                (palette (mod (dec (count palette)) (inc escape-iter)))))
            (.drawRect g x y 1 1))
        (.dispose g)
        img))

(render-text (mandelbrot -2.25 0.75 -1.5 1.5 :width 80 :height 40 :depth 100))

mand

Posta un commento o usa questo indirizzo per il trackback.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: