Подтвердить что ты не робот

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

Мой вопрос: поэтому переменные цифры не должны быть видны последней фразе. Я что-то пропустил здесь?

4b9b3361

Ответ 1

Короткий ответ. Используйте let без in в теле блока do и в части после | в понимании списка. В любом месте используйте let ... in ....


Ключевое слово let используется тремя способами в Haskell.

  • Первая форма - это let-expression.

    let variable = expression in expression
    

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

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

    do statements
       let variable = expression
       statements
    
  • Третий похож на номер 2 и используется внутри понимания списка. Опять же, нет in.

    > [(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.

Ответ 2

Во-первых, почему объятия? платформа Haskell, как правило, рекомендуется использовать для новичков, которые поставляются с GHC.

Теперь выберите ключевое слово let. Простейшая форма этого ключевого слова всегда используется с in.

let {assignments} in {expression}

Например,

let two = 2; three = 3 in two * three

{assignments} находятся только в области видимости в соответствующем {expression}. применяются обычные правила компоновки, что означает, что in должен иметь отступ, по меньшей мере, столько же, сколько let, которое соответствует to, и любые подвыражения, относящиеся к выражению 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. Как упоминается в учебнике, которое вы связали, это может быть полезно, если несколько охранников повторно используют одни и те же выражения.

Ответ 3

В обозначении do вы действительно можете использовать let с и без in. Чтобы он был эквивалентен (в вашем случае я позже покажу пример, где вам нужно добавить второй do и, следовательно, больше отступов), вам нужно отступить его, как вы обнаружили (если вы используете макет - если вы используете явные скобки и точки с запятой, они в точности эквивалентны).

Чтобы понять, почему это эквивалентно, вы должны на самом деле собрать монады (по крайней мере до некоторой степени) и посмотреть на правила обесцвечивания для обозначения do. В частности, код выглядит следующим образом:

do let x = ...
   stmts -- the rest of the do block

переводится на let x = ... in do { stmts }. В вашем случае stmts = print (problem_8 digits). Оценка всего связанного связывания let приводит к действию IO (от print $ ...). И здесь вам нужно понять монады, чтобы интуитивно согласиться с тем, что нет разницы между обозначениями do и "регулярными" языковыми элементами, описывающими вычисление, приводящее к монадическим значениям.

Как для обоих, так и для чего: Ну, let ... in ... имеет широкий спектр приложений (большинство из которых не имеет ничего общего с монадами) и длительная история для загрузки. let без in для обозначения do, с другой стороны, кажется, не что иное, как небольшой кусок синтаксического сахара. Преимущество очевидно: вы можете привязывать результаты чистых (как в, а не монадических) вычислений к имени, не прибегая к бессмысленному val <- return $ ... и не разбивая блок do на два:

do stuff
   let val = ...
    in do more
          stuff $ using val

Причина, по которой вам не нужен дополнительный блок do, для которого следует let, заключается в том, что вы получили только одну строку. Помните, do e есть 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))

Ответ 4

Некоторые новички-новички о "следуют двум одинаковым".

Например, 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. 

Btw, есть приятное объяснение со многими примерами о том, что на самом деле выполняют ключевые слова where и in.