11

Is there any way that input values to a function can be pre-defined so that a user doesn't have to define them each time?

For example, assuming I had a function "zr" which returned a list zeros of size n, such that:

zr 1 = [0]
zr 5 = [0, 0, 0, 0, 0]

And so on.

My current way of implementing it is:

zr :: [Int] -> Int -> [Int]
zr x y
    | length x == y = x
    | otherwise = zr x++[y]

But this isn't particularly elegant as every time I call zr I need to include an empty list as a parameter:

zr [] 5

Any ideas?

Thanks!

Benesh
  • 3,398
  • 1
  • 18
  • 38
Dario
  • 995
  • 1
  • 9
  • 12
  • 1
    This is a good way of implementing the requirement. If you want a nicer API, keep the implementation and write an additional wrapper function that supplies the extra argument. Don't compromise your business code for the sake of the API. – Kilian Foth May 06 '14 at 08:22
  • 1
    @KilianFoth It doesn't implement the requirement. It puts copies of `y` in the result and `y` is not expected to always equal `0`. Actually it doesn't even compile. Changing the recursive case to `zr (x ++ [0]) y` makes it work, but it's still inelegant and inefficient (appending at the end -- although prepending would work just as well -- and recomputing `length` at every step) and we have `replicate` for this. – raymonad May 06 '14 at 09:08
  • Haskell list comprehensions often simplify things and can be brief. zr = \n -> [ (0) | i <- [ 1..n ] ] >>>>>>>>>>>>>>>>> zr 1 = [0] >>>>>>>>>>>>>> zr 5 = [0.0.0.0.0] – fp_mora Feb 21 '18 at 22:26
  • Point free (no variable parameters) would be >>>>>>>>>>>>>>>>>>> zr = flip take $ repeat 0 >>>>>>>>> zr 5 produces the same result – fp_mora Feb 22 '18 at 16:10

3 Answers3

15

I think what you're looking for is partial application:

zr' :: Int -> [Int]
zr' = zr []

If zr is a function that takes a list and an integer, then zr [] is a function that takes only an integer n and returns zr [] n.

In your case, zr' is obviously a "temporary" function (you don't want anyone accidentally calling zr' [1] 4), so you might as well define it locally:

zr :: Int -> [Int]
zr = zr' []
  where
     zr' :: [Int] -> Int -> [Int]
     zr' x y
         | length x == y = x
         | otherwise = zr' x++[0] y
Benesh
  • 3,398
  • 1
  • 18
  • 38
  • Thanks, had thought of this, but was wondering if there was any way I could avoid having to write a second function. – Dario May 06 '14 at 08:25
  • 1
    I don't think it is possible - you'll have to introduce a second binding *somewhere*, since `zr` and your desired function are two different functions. – Benesh May 06 '14 at 08:29
  • Aaah thanks, didn't know you could define a function locally! – Dario May 06 '14 at 08:29
  • 1
    @Dario - you're welcome! This sort of pattern is usually used in FP textbooks when dealing with recursion. I'd like to add that if your function is a "classic" recursion function - you can probably implement it using a higher-order function (`fold`,`map`,`filter`). Try is as an exercise! – Benesh May 06 '14 at 08:34
  • 4
    Also note that `zr = flip replicate 0` – Niklas B. May 06 '14 at 08:38
  • Edit: original version didn't compile. Thanks @raymonad – Benesh May 06 '14 at 09:11
  • 1
    I think it should be noted that forward-recursion like in this answer and OP isn't feasible for the task, as I showed in my post below; forward-recursion is in most cases much longer and harder to read and run than backwards-recursion. (See http://stackoverflow.com/questions/3042954/tail-recursion-vs-forward-recursion) – s-ol May 06 '14 at 12:26
7

Not a Haskell pro myself, but I don't know a way besides defining a shorthand function (zz n = zr [] n).

In your concrete case there however is another solution:
You shouldn't loop the output through parameters, use the return value as means of recursion:

zr 0 = []
zr n = 0:(zr (n-1))

This will result in 0:0:[] for zr 2, which evaluates to [0,0].

Your version is very ineffective because it counts the length of the list on every step, keeps the end value around for no reason (you can decrement it and have 0 as end point) and also have a comparison.

s-ol
  • 1,674
  • 17
  • 28
1

Depending on what you want there are three options:

  1. Passing a record type with function arguments, and defining "defaultParameters" with default values for these arguments (see example in System.Process module)
  2. If you want to allow for just a single optional function parameter, you might consider using Maybe datatype, and pass Nothing if default is good enough.
  3. If you want to have a local defaults that depend on scope, then use GHC extension ImplicitParams. It will let you define defaults for a given code section, that are then used by everything in scope of this section.
Michal Gajda
  • 603
  • 5
  • 13