В Хаскелле, когда мы используем с let?
в следующем коде, последнюю фразу Я могу поставить in перед. Это что-нибудь изменит?
еще один вопрос: если я решу поставить in перед последней фразой Мне нужно отступить?
Я пробовал без отступов и объятий жалуется
последний генератор в do {...} должно быть выражение
import Data.Char
groupsOf _ [] = []
groupsOf n xs =
take n xs : groupsOf n ( tail xs )
problem_8 x = maximum . map product . groupsOf 5 $ x
main = do t <- readFile "p8.log"
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
редактировать
хорошо, поэтому люди, похоже, не понимают, что я говорю. Позволять я перефразирую: являются ли следующие два одинаковых, учитывая контекст выше?
1.
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
2.
let digits = map digitToInt $concat $ lines t
in print $ problem_8 digits
еще один вопрос, касающийся объема Привязок, объявленных в let: Я читал здесь что:
whereположения.
иногда удобно охватывать привязки над несколькими защищенными уравнениями, для чего требуется предложение where:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
обратите внимание, что это невозможно сделать с выражением let, которое охватывает только выражение , которые он прилагает.
мой вопрос: таким образом, переменные цифры не должны быть видны до последней фразы печати. Я что-то пропустил?
4 ответов
короткий ответ: используйте let без in в теле do-блока и в части после | в списке понимания. Нигде, используйте let ... in ....
ключевое слово let используется тремя способами в Haskell.
-
первая форма-это давайте-выражение.
let variable = expression in expressionэто можно использовать везде, где разрешено использование выражения, например,
> (let x = 2 in x*2) + 3 7 -
второй -давайте-заявлением. Эта форма используется только внутри do-notation и не использует
in.do statements let variable = expression statements -
третий похож на номер 2 и используется внутри понимания списка. Опять же, нет!--8-->.
> [(x, y) | x <- [1..3], let y = 2*x] [(1,2),(2,4),(3,6)]эта форма связывает переменную, которая находится в области в последующих генераторах и в выражении перед
|.
причина вашей путаницы здесь в том, что выражения (правильного типа) могут использоваться в качестве операторов внутри do-блока и let .. in .. - это просто такое выражение.
из-за правил отступа haskell, строка отступа дальше, чем предыдущая, означает, что это продолжение предыдущей строки, так что это
do let x = 42 in
foo
анализируется как
do (let x = 42 in foo)
без отступа, вы получаете разбора ошибка:
do (let x = 42 in)
foo
в заключение, никогда не используйте in в списке понимания или do-блока. Это излишне и запутанно, так как эти конструкции уже имеют свою собственную форму let.
во-первых, почему обнимает? The Платформа Haskell обычно рекомендуемые для новичков, который приходит с помощью GHC.
Итак, на letключевое слово. Простейшая форма этого ключевого слова предназначена для всегда использоваться с in.
let {assignments} in {expression}
например,
let two = 2; three = 3 in two * three
на {assignments} are только в объеме в соответствующем {expression}. применяются обычные правила компоновки, это значит, что in должен быть отступ, по крайней мере, столько, сколько let что это соответствует, и любые под-выражения, относящиеся к let выражение также должно быть отступом, по крайней мере, столько же. На самом деле это не 100% правда, но это хорошее эмпирическое правило; правила макета Haskell-это то, к чему вы привыкнете со временем, когда будете читать и писать код Haskell. Просто имейте в виду, что количество отступов является основным способом указать, какой код относится к чему выражение.
Haskell предоставляет два удобных случая, когда вы не нужно писать in: сделайте нотацию и перечислите постижения (на самом деле, постижения монады). Область назначений для этих удобных случаев предопределена.
do foo
let {assignments}
bar
baz
на do нотации,{assignments} в области для любых операторов, которые следуют, в этом случае, bar и baz, но не foo. Как будто мы написано
do foo
let {assignments}
in do bar
baz
перечислите понимания (или действительно, любое понимание монады) desugar в нотации, поэтому они предоставляют аналогичную возможность.
[ baz | foo, let {assignments}, bar ]
на {assignments} в рамках выражения bar и baz, а не foo.
where это несколько другое. Если я не ошибаюсь, размах where строки с определением функции. Так что
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
the {assignments} в этой where предложение имеет доступ к x и y. guard1, guard2, blah1 и blah2 все иметь доступ к {assignments} этой where предложения. Как указано в учебнике, который вы связали, это может быть полезно, если несколько охранников повторно используют одни и те же выражения.
на do нотации, вы действительно можете использовать let и без in. Чтобы он был эквивалентен (в вашем случае я позже покажу пример, где вам нужно добавить второй do и, следовательно, больше отступов), вам нужно отступить, как вы обнаружили (если вы используете макет - если вы используете явные фигурные скобки и точки с запятой, они точно эквивалентны).
понять почему это эквивалентно, вы должны на самом деле grok монады (по крайней мере, в какой-то степени) и посмотрите на правила в результате обессахаривания do нотации. В частности, такой код:
do let x = ...
stmts -- the rest of the do block
переведен на let x = ... in do { stmts }. В твоем случае, stmts = print (problem_8 digits). Оценивая всю обессахаренный let привязка приводит к действию ввода-вывода (от print $ ...). И здесь вам нужно понимание монад, чтобы интуитивно согласиться, что нет никакой разницы между do обозначения и" регулярные " элементы языка, описывающие вычисление, приводящее к монадическим значениям.
а для чего возможно: ну let ... in ... имеет широкий спектр приложений (большинство из которых не имеют ничего общего с монадами в частности), и долгую историю в придачу. let без in на val <- return $ ... и не разделяя do блок в два:
do stuff
let val = ...
in do more
stuff $ using val
причина, по которой вам не нужен дополнительный do блок для того, что следует за let это то, что у вас есть только одна строка. Помни,do e is e.
относительно вашего редактирования: digit быть видимым в следующей строке-это весь смысл. И для этого нет исключений. do обозначение становится одним единственным выражением, и let отлично работает в одном выражении. where нужен только для вещей, которые не выражения.
ради демонстрации, я покажу обессахаренный версию do заблокировать. Если вы еще не слишком знакомы с монадами (что-то вы должны изменить в ближайшее время IMHO), игнорируйте >>= оператор и сосредоточиться на let. Также обратите внимание, что отступы больше не имеют значения.
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
некоторые начинающие заметки о "следуют двум одинаковым".
например, add1 это функция, которая добавляет 1 к номеру:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
это как add1 x = x + inc С заменой inc на 1 от let ключевое слово.
когда вы пытаетесь подавить in ключевое слово
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
у вас есть ошибка разбора.
С документация:
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
кстати, есть хорошее объяснение С много примеров о том, что where и in ключевое слово на самом деле.