Haskell: Где против Let
Я новичок в Haskell, и меня очень смущает Где и Пусть. Кажется, что оба они обеспечивают аналогичную цель. Я прочитал несколько сравнений между Где и Пусть, но у меня возникают проблемы с тем, когда их использовать. Может ли кто-нибудь указать какой-либо контекст или, возможно, несколько примеров, демонстрирующих, когда использовать один над другим?
Где vs. Let
Предложение A where
может быть определено только на уровне определения функции. Обычно это идентично определению let
. Единственное различие заключается в том, что охранники используются. Объем предложения where
распространяется на все защитные устройства. Напротив, объем выражения let
- это только текущее предложение функции и защита, если таковые имеются.
Haskell Cheat Sheet
Haskell Wiki очень подробный и содержит различные случаи, но он использует гипотетические примеры. Я нахожу его объяснения слишком краткими для новичка.
Преимущества Let:
f :: State s a
f = State $ \x -> y
where y = ... x ...
Control.Monad.State
не будет работать, поскольку соответствие шаблона f =, где no x находится в сфере охвата. Напротив, если бы вы началось с let, тогда вы бы не есть проблема.
Haskell Wiki о преимуществах Let
f :: State s a
f = State $ \x ->
let y = ... x ...
in y
Преимущества Where:
f x
| cond1 x = a
| cond2 x = g a
| otherwise = f (h x a)
where
a = w x
f x
= let a = w x
in case () of
_ | cond1 x = a
| cond2 x = g a
| otherwise = f (h x a)
Декларация против выражения
В вики Haskell упоминается, что предложение Где является декларативным, а выражение Пусть выразительно. Помимо стиля, как они работают по-разному?
Declaration style | Expression-style
--------------------------------------+---------------------------------------------
where clause | let expression
arguments LHS: f x = x*x | Lambda abstraction: f = \x -> x*x
Pattern matching: f [] = 0 | case expression: f xs = case xs of [] -> 0
Guards: f [x] | x>0 = 'a' | if expression: f [x] = if x>0 then 'a' else ...
- В первом примере почему Пусть, но Где нет?
- Можно ли применить Где к первому примеру?
- Можно ли применить это к реальным примерам, где переменные представляют собой фактические выражения?
- Существует ли общее правило для использования каждого из них?
Update
Для тех, кто пришел к этой теме позже, я нашел лучшее объяснение, которое можно найти здесь: "" Нежное введение в Haskell".
Пусть выражения.
Haskell позволяют использовать выражения всякий раз, когда вложенный набор привязок обязательный. В качестве простого примера, рассмотреть следующие вопросы:
let y = a*b
f x = (x+y)/y
in f c + f d
Набор привязок, созданных let выражение взаимно рекурсивно и привязки шаблонов считаются ленивыми шаблонов (т.е. они несут подразумеваемую ~). Единственный вид объявлений разрешены типы подписей, привязки функций и шаблон привязок.
Где Классы.
Иногда удобно использовать область привязки над несколькими охраняемыми уравнения, для которых требуется раздел:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
Обратите внимание, что это невозможно сделать с помощью выражения let, которое ограничивается только выражением, которое оно охватывает. Предложение where разрешено только на верхнем уровне набора уравнений или выражения case. Те же свойства и ограничения на привязки в выражениях let применяются к тем, где содержатся предложения. Эти две формы вложенной области кажутся очень схожими, но помните, что выражение let является выражением, тогда как предложение where не является - оно является частью синтаксиса объявлений функций и выражений case.
Ответы
Ответ 1
1: Проблема в примере
f :: State s a
f = State $ \x -> y
where y = ... x ...
- параметр x
. Вещи в предложении where
могут ссылаться только на параметры функции f
(их нет) и вещи во внешних областях.
2: Чтобы использовать where
в первом примере, вы можете ввести вторую именованную функцию
который принимает x
как параметр, например:
f = State f'
f' x = y
where y = ... x ...
или вот так:
f = State f'
where
f' x = y
where y = ... x ...
3: Вот полный пример без ...
:
module StateExample where
data State a s = State (s -> (a, s))
f1 :: State Int (Int, Int)
f1 = State $ \[email protected](a, b) ->
let
hypot = a^2 + b^2
result = (hypot, state)
in result
f2 :: State Int (Int, Int)
f2 = State f
where
f [email protected](a, b) = result
where
hypot = a^2 + b^2
result = (hypot, state)
4: Когда использовать let
или where
, это вопрос вкуса. Я использую let
, чтобы подчеркнуть вычисление (перемещая его вперед) и where
, чтобы подчеркнуть поток программы (перемещая вычисление на спину).
Ответ 2
Несмотря на то, что существует техническая разница в отношении охранников, на которые указывает эфемер, существует также концептуальная разница в том, хотите ли вы поместить основную формулу вверх с дополнительными переменными, определенными ниже (where
), или вы хотите определить все заранее и поместите формулу ниже (let
). Каждый стиль имеет различный акцент, и вы видите, как они используются в математических статьях, учебниках и т.д. Как правило, переменные, которые являются достаточно неинтуитивными, что формула не имеет смысла без них, должны быть определены выше; переменные, которые интуитивно понятны из-за контекста или их имен, должны быть определены ниже. Например, в примере ephemient hasVowel значение vowels
является очевидным и поэтому его не нужно определять выше его использования (без учета того, что let
не будет работать из-за защитника).
Ответ 3
Допустимо:
main = print (1 + (let i = 10 in 2 * i + 1))
Не разрешено:
main = print (1 + (2 * i + 1 where i = 10))
Допустимо:
hasVowel [] = False
hasVowel (x:xs)
| x `elem` vowels = True
| otherwise = False
where vowels = "AEIOUaeiou"
Не является законным: (в отличие от ML)
let vowels = "AEIOUaeiou"
in hasVowel = ...
Ответ 4
Я нашел этот пример из LYHFGG полезным:
ghci> 4 * (let a = 9 in a + 1) + 2
42
let
- это выражение, поэтому вы можете поместить let
в любом месте (!), где могут выполняться выражения.
Другими словами, в приведенном выше примере не можно использовать where
, чтобы просто заменить let
(не используя, возможно, более подробное выражение case
в сочетании с where
),
Ответ 5
К сожалению, большинство ответов здесь слишком технически для новичка.
У LHYFGG есть соответствующая глава -which, которую вы должны прочитать, если вы еще этого не сделали, но по сути:
where
- это просто синтаксическая конструкция (не сахар), которая полезна только при определении функций.
let ... in
- это само выражение, поэтому вы можете использовать их везде, где можете поместить выражение. Будучи самим выражением, его нельзя использовать для привязывания вещей для охранников.
Наконец, вы можете использовать let
и в списках:
calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height
Мы включаем let в понимание списка так же, как и в предикат, только он не фильтрует список, а только привязывает к именам. Имена, определенные в элементе let внутри понимания списка, видны для выходной функции (часть до |
) и всех предикатов и разделов, которые идут после привязки. Таким образом, мы могли бы заставить нашу функцию возвращать только ИМТ людей> = 25: