Haskell: предложение where, ссылающееся на связанные переменные в лямбде - PullRequest
13 голосов
/ 24 января 2012

Я пытаюсь численно интегрировать функцию в Haskell, используя правило трапеции, возвращая антипроизводную, которая принимает аргументы a, b для конечных точек интервала, который должен быть интегрирован.

integrate :: (Float -> Float) -> (Float -> Float -> Float)

integrate f
  = \ a b -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000

В вышесказанном я использую

n - for the number of subintervals
d - for the width of each subinterval

Это почти работает, за исключением связанных аргументов a, b в лямбде. Я получаю сообщение об ошибке:

Not in scope: `b'
Not in scope: `a'

Я могу понять, что область действия a, b ограничена только этим лямбда-выражением, но Есть ли обходной путь в Haskell, чтобы мне не приходилось писать (b-a) / n для каждого вхождения d в приведенном выше?

Ответы [ 5 ]

19 голосов
/ 24 января 2012

Вы думаете, что вам нужно вернуть функцию, которая принимает два Float s и возвращает Float, но на самом деле это ничем не отличается от получения двух дополнительных Float аргументов в вашей функции integrate и использования curry (т.е. просто не предоставляйте их, и тип возвращаемого значения будет Float -> Float -> Float).

Таким образом, вы можете переписать свою функцию следующим образом:

integrate :: (Float -> Float) -> Float -> Float -> Float

integrate f a b
  = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000

Или вы можете использовать let ... in вместоwhere:

integrate f
  = \a b ->
      let d = (b - a / n)
          n = 1000
      in d * sum [ f (a + d * k) | k <- [0..n] ] - d/2.0 * (f a + f b)
4 голосов
/ 24 января 2012

У вас много обходных путей.

Если вы не знаете какого-либо связующего синтаксиса, кроме лямбда-выражений, вы можете сделать это (что мне нравится больше всего из-за его теоретической красоты, но никогда не использовать из-за его синтаксического уродства):

integrate f
  = \a b -> (\d -> d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)) 
             ((b - a) / n)
    where
      n = 1000

Если вам нравятся определения и вы знаете только where -синтаксис, вы можете сделать это:

integrate f = go
  where
    n = 1000
    go a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
      where
        d = (b - a) / n

Если вы также знаете let -синтаксис, вы можете сделать это:

integrate f = 
  \a b -> let d = (b - a) / n 
          in d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
  where
    n = 1000

Наконец, если вы помните, что a -> (b -> c -> d) совпадает с a -> b -> c -> d, вы можете сделать очевидное:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
  where
    n = 1000
    d = (b - a) / n
4 голосов
/ 24 января 2012

Конечно.

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where 
      d = (b - a) / n
      n = 1000
2 голосов
/ 24 января 2012

Если вы настаиваете на том, где:

integrate f = \a b -> case () of
    () ->  d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
           where 
               d = (b - a) / n
               n = 1000

Выглядит довольно мило, не так ли?Чтобы дело выглядело более мотивированным:

integrate f = \a b -> case (f a + f b) of
    fs ->  d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * fs
           where 
               d = (b - a) / n
               n = 1000
1 голос
/ 24 января 2012

попробуй:

integrate f a b = d * sum [ f (a + d*k) | k <- [0..n] ] - d/2.0 * (f a + f b)
    where
      d = (b - a) / n
      n = 1000
...