## Haskell – 31 – funzioni di ordine superiore – 8 Continuo da qui, copio qui, scrollare fino a “Higher-order Functions Provide Flexibility”.

Le funzioni di ordine superiore forniscono flessibilità
We have seen that the use of higher-order functions can increase code reuse and lead to concise, more readable code. We have yet to discuss that, in addition, higher-order functions can provide flexibility. To illustrate this benefit, let us return to the Pythagorean trees from Spirals, Snowflakes & Trees: Recursion in Pictures [video che trovate sul tutorial che sto seguendo, qui]. The exercises in that chapter focus on extending the given code, such that multi-coloured trees can be produced (as opposed to creating a single path that represents the entire tree, which only let’s us display the tree in one single colour). We might implement this as follows:

``````fade :: Colour -> Colour
| opacityC == 0 = col
| otherwise     = (redC, greenC, blueC, opacityC - 20)

colouredFTree :: Int -> Float -> Colour -> Line -> Picture
colouredFTree n factor colour line = fT n colour line
where
fT 0 colour line = [(colour, [fst line, snd line])]
fT n colour line = [(colour,[p2, p3])]
++ [(colour,[p4, p1])]
++ fT (n-1) colour' (p5, p3)
++ fT (n-1) colour' (p4, p5)
where
[p1,p2,p3,p4,_] = polygon 4 line
(_, p5)         = rotateLine (factor * pi)
\$ (\(x,y) -> (y,x))
\$ scaleLine 0.5 (p3, p4)``````

Using this function in

`drawPicture 3 (colouredFTree 14 0.55 red line3)`

produces the following picture, where we incrementally reduce the opacity of the colour as the recursion progresses. Now, there are a lot of other, more interesting colour effects than simply reducing the opacity of a colour. One option is to bleach the initial colour:

``````bleach :: Colour -> Colour
bleach (redC, greenC, blueC, opacityC)
= (min 255 (redC + 18),
min 255 (greenC + 18),
min 255 (blueC + 18),
opacityC)

colouredFTree :: Int -> Float -> Colour -> Line -> Picture
colouredFTree n factor colour line = fT n colour line
where
fT 0 colour line = [(colour, [fst line, snd line])]
fT n colour line = [(colour,[p2, p3])]
++ [(colour,[p4, p1])]
++ fT (n-1) colour' (p5, p3)
++ fT (n-1) colour' (p4, p5)
where
colour'         = bleach colour
[p1,p2,p3,p4,_] = polygon 4 line
(_, p5)         = rotateLine (factor * pi)
\$ (\(x, y) -> (y, x))
\$ scaleLine 0.5 (p3, p4)``````

With this definition, we can generate the following pictures, by using red, green and black, respectively, as initial colours: Clearly, there are countless other possibilities, and we definitely don’t want to rewrite the code, or define a new variant of the existing code, every time we try something new. Instead of committing to a fixed method to calculate the colour for each recursive step, we can pass a function which takes care of this decision as higher-order parameter to the tree construction function.

What should the type of that function parameter be? In the two examples above, we used a function of type `Colour -> Colour`, so this seems a reasonable choice. If we want to avoid having to pass an initial colour in addition to the function, we can alternatively model it as function from recursion depth to colour of type `Int -> Colour`.

``````colouredFTree :: Int -> Float -> (Int -> Colour) -> Line -> Picture
colouredFTree n factor colourFun line = fT n line
where
fT 0  line = [(colourFun 0, [fst line, snd line])]
fT n  line = [(colourFun n, [p2, p3])]
++ [(colourFun n,[p4, p1])]
++ fT (n-1) (p5, p3)
++ fT (n-1) (p4, p5)
where
[p1,p2,p3,p4,_] = polygon 4 line
(_, p5)         = rotateLine (factor * pi)
\$ (\(x, y) -> (y, x))
\$ scaleLine 0.5 (p3, p4)``````

For example, by passing these functions as arguments to `colouredFTree`:

``````magentaToWhite, toBlue1, toBlue2 :: Int -> Colour
magentaToWhite n = (127 + (18 - n) * 7, (18 - n) * 15, 255, 255)
toBlue1 n        = (255 - (16 - n) * 13, 255 - (16 - n) * 13, 214, 255)
toBlue2 n        = (51 + n * 8, 255 + 14 * n, 255, 255)``````

we generate the following images. Another way to create more interesting, organically looking trees is to vary factor argument of `colouredFTree`, which determines the ratio between the size of the left and right subtree in each recursive step. As with the `colour`, we replace the constant value by a function that characterised the change of the factor in dependence on the recursion depth:

``````colouredFTree :: Int -> (Int -> Float) -> (Int -> Colour) -> Line -> Picture
colouredFTree n factorFun colourFun line = fT n line
where
fT 0  line = [(colourFun 0, [fst line, snd line])]
fT n  line = [(colourFun n, [p2, p3])]
++ [(colourFun n,[p4, p1])]
++ fT (n-1) (p5, p3)
++ fT (n-1) (p4, p5)
where
[p1,p2,p3,p4,_] = polygon 4 line
(_, p5)         = rotateLine ((factorFun n) * pi)
\$ (\(x, y) -> (y, x))
\$ scaleLine 0.5 (p3, p4)``````

This enables us to generate a rather varied collection of fractals trees with the one definition of `colouredFTree` using

``````toggleFactor2 factor n = if (n `mod` 2) == 0 then factor else (1 - factor)
toggleFactor5 factor n = if (n `mod` 5) == 0 then factor else (1 - factor)
shiftFactor factor n   = factor + (fromIntegral (16 - n)) * 0.025``````

as function parameters partially applied as `toggleFactor2 0.7`, `toggleFactor5 0.7`, and `shiftFactor 0.5`, all with an iteration depth of 16. What if we don’t want to vary one of the two function parameters as the recursion progresses (to get some of our initial fractal trees)? In this case, we simply pass a constant function as an argument — i.e., a function whose result is independent of its argument:

`colouredFTree n (\_ -> 0.5) (\_ -> red) line`

Constant functions are often useful; hence, the Haskell `Prelude` provides a combinator to construct them (without an explicit lambda abstraction):

`const :: a -> b -> a`
`const x _ = x`

This function is always used with partial application; for example, we can rephrase the previous invocation of `colouredFTree` as

`colouredFTree n (const 0.5) (const red) line`

While the second version is not much more concise, it is considered better style.

Uhmmmm… esempio complesso, devo studiarmelo per capire cosa succede; forse era meglio uno più semplice dove si evidenziava le parti varianti.

🤩

Posta un commento o usa questo indirizzo per il trackback.