3

What is the best way to call a function of two variables on more than two variables?

For example given the function max

Prelude> :t max
max :: Ord a => a -> a -> a

if I want the maximum of the variables x,y,z I can call

max (max x y) z

but it is verbose for large numbers of elements.

One way would be to do it recursively on a list (using the an example from "Learn You a Haskell for Great Good"

maximum :: Ord x => [x] -> x; 
maximum [] = error "maximum of empty list" 
maximum (x:[]) = x
maximum (x:xs) = max x (maximum(xs))

So using this I would have to create an array and then a function like this to operate on arrays. Is there a better way to do this?

2
  • 1
    In this case, there is already a maximum function in the Prelude. Here is how it is implemented (and note that it is specialised for Ints and Integers). Commented Mar 18, 2013 at 12:07
  • Note that [x] is not an array, it is a list, particularly, an immutable singly-linked list. You can find implementations of arrays at Data.Array and Data.Vector. Commented Mar 18, 2013 at 17:34

3 Answers 3

4

The pattern you are talking about are not a "function which operate on an array", just a common recursive pattern.

Of course there are function to deal with recursive pattern, as fold for example.
Your maximum function could then be redefined as,

maximum xs = foldl' max 0 xs -- assuming all the value are upper than 0.   

if the restriction on the value of the list bother you can use a variant of foldl', foldl1' which will replace the seed (0 upper) by, the first element of list.

maximum xs = foldl1' max xs

Almost every recursive structure is foldable (List, Tree, Graph...).
But their are other recursive pattern, which can be exploited using map or filter.

Anyway a good introduction on recursion using fold,
A tutorial on the universality and expressiveness of fold

Sign up to request clarification or add additional context in comments.

4 Comments

You should consider using foldl1' in this case to avoid space leaks
You're right, I intentionally focus my answer to deal about recursion, then space optimization, strictness ... have been put on the side. I guess it better to proceed step by step and not to confuse people with extract information which treat issue out of the scope of the initial question.
I'm not sure. IMHO it's reasonably good advice to always use foldl' and almost never foldl, so why not tell the beginners exactly this, without even mentioning space issues etc.?
I agree, foldl is pretty much just evil. If you want to keep it simple, explain foldr instead.
2

Just to add to zurgl's answer, Haskell has a type class Foldable for containers. There is an utility method

maximum :: (Foldable t, Ord a) => t a -> a

defined simply as

maximum = foldr1 max

So for whatever container type that implements Foldable (denoted by t) and whatever element type with ordering (a) this single function can be used. In other words, if a data type implements folding operation over its elements, any aggregating function over it can be defined in a similar way.

Comments

1

What is the best way to call a function of two variables on more than two variables?

Well let's see here. In Racket, I would say use a macro.

#lang racket

(define-syntax apply*
  (syntax-rules ()
    [(apply* f x y)
     (f x y)]
    [(apply* f x y rest ...)
     (apply* f (f x y) rest ...)]))

(define (plus a b) (+ a b))
(apply* plus 1 2 3 4 5) ;; => 15

Here we have deemed that the apply* form takes a function of two arguments, and calls it on an arbitrarily long list of arguments. This will literally expand (apply* max x y z) into (max (max x y) z), allowing you to write the pretty code and letting the macro expander turn it into the verbose code.


However, Haskell macros are annoying to deal with, and rather unidiomatic. So let's use a function instead. What will this function look like?

applyStar :: (a -> a -> a) -> [a] -> a

The first argument is a 2-arg function, which takes two things of the same type, and spits out a thing of that type. This we can see from the desired result: max (max x y) z, the output must be the same type as the inputs. Haskell doesn't let you define functions of variable arity, so in order to simulate that, we write a function that takes a list of arguments, [a].

Stop. Hoogle time! If you search for the type signature (a -> a -> a) -> [a] -> a at http://haskell.org/hoogle , then you'll find that this is called foldl1 and is already implemented for you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.