W Haskell, kiedy użyjemy in Z let?
W poniższym kodzie, ostatnią frazę mogę umieścić in
z przodu. Czy to coś zmieni?
Kolejne pytanie: jeśli zdecyduję się umieścić in
przed ostatnim zdaniem, czy muszę je wciąć?
Próbowałem bez wcięcia i przytula skarży się
Ostatni generator w do {...} must be an expression
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
Edit
OK, więc ludzie nie rozumieją, co mówię. Powiem inaczej.: są następujące dwa takie same, biorąc pod uwagę kontekst powyżej?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
Kolejne pytanie dotyczące zakresu wiązań zadeklarowanych w let
: przeczytałem tutaj , że:
where
klauzule.
Czasami wygodnie jest zakreślić wiązania na kilku strzeżonych równaniach, co wymaga klauzuli where:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
Zauważ, że nie można tego zrobić za pomocą wyrażenia let, które obejmuje tylko wyrażenie , które zawiera.
Moje pytanie: tak więc zmienne cyfry nie powinny być widoczne dla ostatniej drukowanej frazy. Czy coś mnie ominęło?
4 answers
Krótka odpowiedź : użyj let
Bez in
w ciele bloku do, a w części po |
w zrozumieniu listy. Gdziekolwiek indziej, użyj let ... in ...
.
Słowo kluczowe let
jest używane w Haskell na trzy sposoby.
-
Pierwsza forma to let-expression.
let variable = expression in expression
Może być stosowany wszędzie tam, gdzie dozwolone jest wyrażenie, np.
> (let x = 2 in x*2) + 3 7
-
Drugi jest let-statement. Ten formularz jest używany tylko wewnątrz do-notacji i nie używa
in
.do statements let variable = expression statements
-
Trzeci jest podobny do numeru 2 i jest używany wewnątrz list compensions. Ponownie, nie
in
.> [(x, y) | x <- [1..3], let y = 2*x] [(1,2),(2,4),(3,6)]
Forma ta wiąże zmienną, która znajduje się w zakresie w kolejnych generatorach oraz w wyrażeniu przed
|
.
Powodem twojego zamieszania jest to, że wyrażenia (właściwego typu) mogą być używane jako instrukcje w obrębie bloku do, a let .. in ..
jest po prostu ekspresja.
do let x = 42 in
foo
Jest parsowany jako
do (let x = 42 in foo)
Bez wcięcia pojawia się błąd parsowania:
do (let x = 42 in)
foo
Podsumowując, nigdy nie używaj in
w zrozumieniu listy lub bloku do. Jest to niezauważalne i mylące, ponieważ konstrukcje te mają już swoją własną formę let
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-25 22:41:15
Po Pierwsze, po co się przytulać? Platforma Haskell jest ogólnie zalecanym sposobem dla początkujących, który jest dostarczany z GHC.
A teraz przejdźmy do słowa kluczowego let
. Najprostszą formą tego słowa kluczowego jest zawsze {[41] } być używane z in
.
let {assignments} in {expression}
Na przykład,
let two = 2; three = 3 in two * three
{assignments}
są tylko w zakresie odpowiadającym {expression}
. stosuje się regularne zasady układu, co oznacza, że in
musi być wcięte co najmniej tak samo jak let
że odpowiada, a wszelkie pod-wyrażenia odnoszące się do wyrażenia let
muszą również być wcięte co najmniej tak samo. Nie jest to w rzeczywistości 100% prawda, ale jest dobrą zasadą; reguły układu Haskell są czymś, do czego po prostu przyzwyczaisz się z czasem, gdy czytasz i piszesz kod Haskell. Należy pamiętać, że ilość wcięć jest głównym sposobem wskazywania, który kod odnosi się do jakiego wyrażenia.
in
: składanie notacji i list (właściwie składanie monad). Zakres zadań dla tych wygodnych przypadków jest z góry określony.
do foo
let {assignments}
bar
baz
Dla notacji do
, {assignments}
są w zakresie wszelkich twierdzeń, które następują, w tym przypadku, bar
i baz
, ale nie foo
. To tak, jakbyśmy napisali
do foo
let {assignments}
in do bar
baz
Składnia listy (a właściwie każde rozumienie monad) desugar w notację do, więc zapewniają podobną funkcję.
[ baz | foo, let {assignments}, bar ]
The {assignments}
są w zakresie dla wyrażeń bar
i baz
, ale nie dla foo
.
where
jest nieco inny. Jeśli się nie mylę, zakres where
odpowiada określonej definicji funkcji. Więc
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
{assignments}
w tym where
klauzuli mają dostęp do x
i y
. guard1
, guard2
, blah1
, oraz blah2
Wszystkie mają dostęp do {assignments}
tej klauzuli where
. Jak wspomniano w linkowanym samouczku, może to być pomocne, jeśli wielu strażników ponownie użyje tego samego wyrażenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-25 23:00:03
W notacji do
można rzeczywiście używać let
z i bez in
. Aby to było równoważne (w Twoim przypadku, pokażę później przykład, gdzie trzeba dodać drugi do
, a tym samym więcej wcięć), musisz wciąć go tak, jak odkryłeś (jeśli używasz layout - jeśli używasz jawnych nawiasów i średników, są one dokładnie równoważne).
Aby zrozumieć dlaczego jest to równoznaczne, trzeba faktycznie grok monad (przynajmniej w pewnym stopniu) i przyjrzeć się zasadom desugaringu dla do
notacji. W szczególności, kod taki jak ten:
do let x = ...
stmts -- the rest of the do block
Jest tłumaczone na let x = ... in do { stmts }
. W Twoim przypadku, stmts = print (problem_8 digits)
. Ewaluacja całego desugared let
wiązania skutkuje działaniem IO (z print $ ...
). I tutaj, trzeba zrozumieć monady, aby intuicyjnie zgodzić się, że nie ma różnicy między notacjami do
a "regularnymi" elementami języka opisującymi obliczenia skutkujące wartościami monadycznymi.
Jeśli chodzi o oba dlaczego są możliwe: Cóż, let ... in ...
ma szeroki zakres zastosowań (większość które nie mają nic wspólnego z monadami w szczególności), i długą historię do rozruchu. let
Bez in
dla do
notacja, z drugiej strony, wydaje się być niczym innym jak małym kawałkiem cukru składniowego. Zaleta jest oczywista: można powiązać wyniki czystych (jak w, nie monadycznych) obliczeń z nazwą bez uciekania się do bezsensownego val <- return $ ...
i bez dzielenia do
bloku na dwa:
do stuff
let val = ...
in do more
stuff $ using val
Powodem, dla którego nie potrzebujesz dodatkowego bloku do
do tego, co następuje let
, jest to, że mam tylko jedną linię. Pamiętaj, do e
jest e
.
W odniesieniu do Twojej edycji: digit
bycie widocznym w następnej linii jest całym punktem. I nie ma dla tego wyjątku. do
notacja staje się jednym wyrażeniem i let
działa dobrze w jednym wyrażeniu. where
jest potrzebny tylko do rzeczy, które nie są wyrażeniami.
>>=
i skupić się na let
. Zauważ również, że wcięcie nie ma już znaczenia.
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
Niektóre notatki dla początkujących o "podążają za dwoma tymi samymi".
Na przykład, {[3] } jest funkcją, która dodaje 1 do liczby:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
Więc, to jest jak {[4] } z podstawieniem inc przez 1 ze słowa kluczowego let
.
Gdy próbujesz stłumić in
słowo kluczowe
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
Masz błąd parse.
From documentation :
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
Btw, są ładne Wyjaśnienie z wieloma przykładami na temat tego, co rzeczywiście robią where
i in
słowa kluczowe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-25 22:37:05