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

  1. первая форма-это давайте-выражение.

    let variable = expression in expression
    

    это можно использовать везде, где разрешено использование выражения, например,

    > (let x = 2 in x*2) + 3
    7
    
  2. второй -давайте-заявлением. Эта форма используется только внутри do-notation и не использует in.

    do statements
       let variable = expression
       statements
    
  3. третий похож на номер 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 ключевое слово на самом деле.