• No se han encontrado resultados

3. MATERIALES Y MÉTODOS

3.9 Análisis económico

4.1.2.2 Número de granos vanos por espiga

Syntactic Magic

This chapter introduces several more of Haskell’s syntactic devices that fa- ciliate writing concise and intuitive programs. These devices will be used frequently in the remainder of the text.

5.1

Sections

The use of currying was introduced in Chapter3 as a way to simplify pro- grams. This is a syntactic device that relies on the way that normal functions are applied, and how those applications are parsed.

With a bit more syntax, one can also curry applications of infix operators such as (+). This syntax is called a section, and the idea is that, in an expression such as (x+y), one can omit either thex or they, and the result (with the parentheses still intact) is a function of that missing argument. If both variables are omitted, it is a function of two arguments. In other words, the expressions (x+), (+y) and (+) are equivalent, respectively, to the functions:

f1y =x+y f2x =x+y f3x y =x+y

For example, suppose one wishes to remove all absolute pitches greater than 99 from a list, perhaps because everything above that value is assumed to be unplayable. There is a pre-defined function in Haskell that can help to achieve this:

filter:: (a →Bool)[a][a]

filter p xs returns a list for which each element x satisfies the predicate p; i.e.p x isTrue.

Usingfilter, one can then write:

playable :: [AbsPitch][AbsPitch] playable xs =lettest ap=ap<100

infilter test xs

But using a section, one can write this more succinctly as: playable :: [AbsPitch][AbsPitch]

playable xs =filter (<100)xs

which can be further simplified using currying: playable:: [AbsPitch][AbsPitch] playable =filter (<100)

This is an extremely concise definition. As you gain experience with higher-order functions you will not only be able to start writing definitions such as this directly, but you will also startthinkingin “higher-order” terms. Many more examples of this kind of reasoning will appear throughout the text.

Exercise 5.1 Define a function twice that, given a function f, returns a function that appliesf twice to its argument. For example:

(twice (+1)) 24

What is the principal type of twice? Describe what twice twice does, and give an example of its use. Also consider the functionstwice twice twice and twice (twice twice)?

Exercise 5.2 Generalize twice defined in the previous exercise by defining a function power that takes a function f and an integer n, and returns a function that applies the functionf to its argumentn times. For example:

power (+2) 5 1 =11

5.2

Anonymous Functions

Another way to define a function in Haskell is in some sense the most funda- mental: it is called ananonymous function, orlambda expression (since the concept is drawn directly from Church’s lambda calculus [Chu41]). The idea is that functions are values, just like numbers and characters and strings, and therefore there should be a way to create them without having to give them a name. As a simple example, an anonymous function that incre- ments its numeric argument by one can be writtenλx →x+ 1. Anonymous functions are most useful in situations where you don’t wish to name them, which is why they are called “anonymous.” Anonymity is a property also shared by sections, but sections can only be derived from an existing infix operator.

Details: The typesetting used in this textbook prints an actual Greek lambda character, but in writing λx x + 1 in your programs you will have to type “\x -> x+1” instead.

As another example, to raise the pitch of every element in a list of pitches ps by an octave, one could write:

map(λp →pitch (absPitch p+ 12))ps

An even better example is an anonymous function that pattern-matches its argument, as in the following, which doubles the duration of every note in a list of notesns:

map(λ(Note d p)→Note (2∗d)p)ns

Details: Anonymous functions can only perform one match against an argument. That is, you cannot stack together several anonymous functions to define one function, as you can with equations.

Anonymous functions are considered most fundamental because defini- tions such as that forsimple given in Chapter 1:

simple x y z =x∗(y +z) can be written instead as:

simple =λx y z →x∗(y+z)

Details: λx y z →exp is shorthand for λx →λy →λz →exp.

One can also use anonymous functions to explain precisely the behavior of sections. In particular, note that:

(x+)⇒λy →x +y (+y)⇒λx →x+y (+) ⇒λx y →x+y

Exercise 5.3 Suppose one defines a functionfixas: fix f =f (fix f)

What is the principal type offix? (This is tricky!) Suppose further that one has a recursive function:

remainder ::Integer →Integer →Integer remainder a b =if a<bthena

elseremainder (a−b)b

Rewrite this function usingfix so that it is not recursive. (Also tricky!) Do you think that this process can be applied toany recursive function?

5.3

List Comprehensions

Haskell has a convenient and intuitive way to define a list in such a way that it resembles the definition of a set in mathematics. For example, recall in the last chapter the definition of the functionaddDur:

addDur ::Dur [Dur →Music a]→Music a addDur d ns=letf n =n d

inline (map f ns)

Herens is a list of notes, each of which does not have a duration yet assigned to it. If one thinks of this as a set, one might be led to write the following solution in mathematical notation:

which can be read, “the set of all notesn d such thatn is an element ofns.” Indeed, using a Haskelllist comprehension one can write almost exactly the same thing:

[n d |n ←ns]

The difference, of course, is that the above expression generates an (ordered) list in Haskell, not an (unordered) set in mathematics.

List comprehensions allow one to rewrite the definition of addDur much more succinctly and elegantly:

addDur ::Dur [Dur →Music a]→Music a addDur d ns=line[n d |n ←ns]

Details: Liberty is again taken in type-setting by using the symbolto mean “is an element of.” When writing your programs, you will have to type “<-” instead. The expression[exp |x ←xs]is actually shorthand for the expressionmapx exp)xs. The formx ←xs is called a generator, and in general more than one is allowed, as in:

[(x,y)|x [0,1,2],y [’a’,’b’]]

which evaluates to the list:

[(0,’a’),(0,’b’),(1,’a’),(1,’b’),(2,’a’),(2,’b’)]

The order here is important; that is, note that the left-most generator changes least quickly.

It is also possible to filter values as they are generated; for example, one can modify the above example to eliminate the odd integers in the first list:

[(x,y)|x [0,1,2],even x,y [’a’,’b’]]

whereeven n returnsTrue ifn is even. This example evaluates to:

Details: When reasoning about list comprehensions (e.g. when doing proof by calculation), one can use the following syntactic translation into pure functions:

[e|True] = [e] [e|q] = [e|q,True] [e|b,qs] =if bthen[e|qs]else[ ] [e|p←xs,qs] =letok p= [e |qs] ok = [ ] inconcatMap ok xs

[e|letdecls,qs] =letdecls in[e|qs]

whereqis a single qualifier,qsis a sequence of qualifiers,bis a Boolean,pis a pat- tern, anddecls is a sequence of variable bindings (a feature of list comprehensions not explained earlier).

5.3.1 Arithmetic Sequences

Another convenient syntax for lists whose elements can be enumerated is called anarithmetic sequence. For example, the arithmetic sequence [1. .10 ] is equivalent to the list:

[1,2,3,4,5,6,7,8,9,10 ]

There are actually four different versions of arithmetic sequences, some of which generateinfinitelists (whose use will be discussed in a later chapter). In the following, let a =n−n:

[n. .] -- infinite listn,n+ 1,n+ 2, ... [n,n. .] -- infinite listn,n+a,n+ 2∗a, ... [n. .m] -- finite list n,n+ 1, n+ 2, ...,m [n,n. .m] -- finite list n,n+a,n+ 2∗a, ...,m

Arithmetic sequences are discussed in greater detail in AppendixB.

Exercise 5.4 Using list comprehensions, define a function:

apPairs:: [AbsPitch][AbsPitch][(AbsPitch,AbsPitch)]

such that apPairs aps1 aps2 is a list of all combinations of the absolute pitches inaps1andaps2. Furthermore, for each pair (ap1,ap2) in the result, the absolute value ofap1−ap2must be greater than two and less than eight. Finally, write a function to turn the result ofapPairs into aMusic Pitch value by playing each pair of pitches in parallel, and stringing them all together sequentially. Try varying the rhythm by, for example, using an

g f

y = f (g x) = (f ƕg) x

x y

Figure 5.1: Gluing Two Functions Together

eighth note when the first absolute pitch is odd, and a sixteenth note when it is even, or some other criterion.

Test your functions by using arithemtic sequences to generate the two lists of arguments given to apPairs.

5.4

Function Composition

An example of polymorphism that has nothing to do with data structures arises from the desire to take two functions f and g and “glue them to- gether,” yielding another functionhthat first appliesgto its argument, and then appliesf to that result. This is called functioncomposition (just as in mathematics), and Haskell pre-defines a simple infix operator () to achieve it, as follows:

() :: (b→c)→(a →b)→a →c (f ◦g)x =f (g x)

Details: The symbol for function composition is typeset in this textbook as , which is consistent with mathematical convention. When writing your programs, however, you will have to use a period, as in “f . g”.

Note the type of the operator (); it is completely polymorphic. Note also that the result of the first function to be applied—some typeb—must be the same as the type of the argument to the second function to be applied. Pictorially, if one thinks of a function as a black box that takes input at one end and returns some output at the other, function composition is like connecting two boxes together, end to end, as shown in Figure5.1.

The ability to compose functions using () is quite handy. For example, recall the last version ofhList:

hList d ps =line (map (hNote d)ps)

One can do two simplifications here. First, rewrite the right-hand side using function composition:

hList d ps = (line◦map (hNote d))ps Then, use currying simplification:

hList d =line◦map (hNote d)

Documento similar