В Хаскелле, когда мы используем с 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
ключевое слово на самом деле.