Haskell: Nerede ve Let


117

Haskell'de yeniyim ve Where vs. Let ile kafam çok karışık . İkisi de benzer bir amaç sağlıyor gibi görünüyor. Nerede ve Let arasında birkaç karşılaştırma okudum ancak her birini ne zaman kullanacağımı ayırt etmekte güçlük çekiyorum. Biri lütfen bir bağlam veya belki birini diğerinin üzerine ne zaman kullanacağını gösteren birkaç örnek verebilir mi?

Let vs Let

Bir whereyan tümce, yalnızca bir işlev tanımı düzeyinde tanımlanabilir. Genellikle bu, lettanımın kapsamıyla aynıdır . Tek fark, korumaların kullanıldığı zamandır . whereMaddenin kapsamı tüm korumaları kapsar. Buna karşılık, bir letifadenin kapsamı , varsa, yalnızca geçerli işlev cümlesi ve korumadır.

Haskell Hile Sayfası

Haskell Wiki çok detaylı ve çeşitli durumlarda sağlar ama varsayımsal örnekler kullanır. Yeni başlayanlar için açıklamalarını çok kısa buluyorum.

Let Avantajları :

f :: State s a
f = State $ \x -> y
   where y = ... x ...

Control.Monad.State

çalışmayacaktır, çünkü burada, kapsamda hiçbir x olmadığında, f = eşleme kalıbını ifade eder. Aksine, Let ile başlasaydın, o zaman sorun yaşamazdın.

Let Avantajları için Haskell Wiki

f :: State s a
f = State $ \x ->
   let y = ... x ...
   in  y

Nerede Olmanın Avantajları :

f x
  | cond1 x   = a
  | cond2 x   = g a
  | otherwise = f (h x a)
  where
    a = w x

f x
  = let a = w x
    in case () of
        _ | cond1 x   = a
          | cond2 x   = g a
          | otherwise = f (h x a)

Beyan ve İfade

Haskell wiki , Let ifadesinin anlamlı iken Where cümlesinin bildirimsel olduğundan bahseder . Tarzın dışında nasıl farklı performans sergiliyorlar?

Declaration style                     | Expression-style
--------------------------------------+---------------------------------------------
where clause                          | let expression
arguments LHS:     f x = x*x          | Lambda abstraction: f = \x -> x*x
Pattern matching:  f [] = 0           | case expression:    f xs = case xs of [] -> 0
Guards:            f [x] | x>0 = 'a'  | if expression:      f [x] = if x>0 then 'a' else ...
  1. İlk örnekte izin ver neden kapsam dahilindedir ama nerede değildir?
  2. İlk örneğe nereye başvurmak mümkün mü ?
  3. Bazıları bunu değişkenlerin gerçek ifadeleri temsil ettiği gerçek örneklere uygulayabilir mi?
  4. Her birinin ne zaman kullanılacağına dair genel bir kural var mı?

Güncelleme

Bu konuya sonradan gelenler için en iyi açıklamayı burada buldum: " Haskell'e Nazik Bir Giriş ".

İfadeler edelim.

Haskell'in let ifadeleri, iç içe geçmiş bir dizi bağlama gerektiğinde kullanışlıdır. Basit bir örnek olarak şunları düşünün:

let y   = a*b
    f x = (x+y)/y
in f c + f d

Bir let ifadesi tarafından oluşturulan bağlar kümesi karşılıklı olarak özyinelemelidir ve desen bağlamaları tembel desenler olarak ele alınır (yani örtük bir ~ taşırlar). İzin verilen tek bildirim türü, tür imzaları, işlev bağlamaları ve desen bağlamalarıdır.

Cümleler nerede.

Bazen, bir where cümlesi gerektiren birkaç korumalı denklem üzerinden bağlamaların kapsamını almak uygundur:

f x y  |  y>z           =  ...
       |  y==z          =  ...
       |  y<z           =  ...
     where z = x*x

Bunun, yalnızca içerdiği ifadeyi kapsayan bir let ifadesi ile yapılamayacağına dikkat edin. Where cümlesine yalnızca bir dizi denklemin veya durum ifadesinin en üst düzeyinde izin verilir. Let ifadelerindeki bağlamalarla ilgili aynı özellikler ve kısıtlamalar where cümleleri için de geçerlidir. Bu iki iç içe geçmiş kapsam biçimi çok benzer görünür, ancak let ifadesinin bir ifade olduğunu, oysa bir where cümlesinin olmadığını unutmayın - işlev bildirimleri ve durum ifadelerinin sözdiziminin bir parçasıdır.


9
Ben arasındaki fark ile şaşırmıştı letve whereben ilk Haskell öğrenmeye başladı. Bence bunu anlamanın en iyi yolu, ikisi arasında çok az fark olduğunu ve bu nedenle endişelenecek bir şey olmadığını fark etmektir. Anlamı whereaçısından verilen letçok basit bir mekanik dönüşüm yoluyla. Haskell.org/onlinereport/decls.html#sect4.4.3.2 adresine bakın . Bu dönüşüm yalnızca notasyonel kolaylık için var, gerçekten.
Tom Ellis

İlk olarak neyi tanımlamak istediğime bağlı olarak genellikle birini veya diğerini kullanırım. Örneğin, insanlar genellikle işlevleri kullanır ve sonra bunları nerede tanımlarlar. İzin, bir tür zorunlu görünümlü işlev istiyorsa kullanılır.
PyRulez

@Tom Ellis, Tom, bahsettiğiniz bağlantıyı anlamaya çalışıyordum ama benim için çok zordu, bu basit dönüşümü sadece ölümlülere açıklayabilir misiniz?
jhegedus

1
@jhegedus: f = body where x = xbody; y = ybody ...meanf = let x = xbody; y = ybody ... in body
Tom Ellis

Teşekkürler Tom! Diğer tarafa gidebilir mi? Let ifadesini bir case .... of ... whereşekilde ifadeye dönüştürmek mümkün müdür ? Bunun hakkında emin değilim.
jhegedus

Yanıtlar:


39

1: Örnekteki sorun

f :: State s a
f = State $ \x -> y
    where y = ... x ...

parametredir x. Tümcecikteki şeyler wheresadece fonksiyonun parametrelerine f(hiçbiri yoktur) ve dış kapsamlardaki şeylere atıfta bulunabilir .

2: whereİlk örnekte a'yı kullanmak için x, aşağıdaki gibi parametre olarak alan ikinci bir adlandırılmış işlevi tanıtabilirsiniz :

f = State f'
f' x = y
    where y = ... x ...

veya bunun gibi:

f = State f'
    where
    f' x = y
        where y = ... x ...

3: Burada, ...'ler olmadan eksiksiz bir örnek verilmiştir :

module StateExample where

data State a s = State (s -> (a, s))

f1 :: State Int (Int, Int)
f1 = State $ \state@(a, b) ->
    let
        hypot = a^2 + b^2
        result = (hypot, state)
    in result

f2 :: State Int (Int, Int)
f2 = State f
    where
    f state@(a, b) = result
        where
        hypot = a^2 + b^2
        result = (hypot, state)

4: Ne zaman kullanılmalı letya whereda zevk meselesidir. Kullandığım let(öne doğru hareket ettirecek) hesaplama vurgulamak ve where(arkasına hesaplama hareket ettirerek) programı akışını vurgulamak.


2
"Where cümlesindeki şeyler sadece f fonksiyonunun parametrelerine (hiçbiri yoktur) ve dış kapsamlardaki şeylere başvurabilir." - Bu bana gerçekten açıklığa kavuşuyor.

28

Ephemient'in işaret ettiği korumalarla ilgili teknik bir fark olsa da, ana formülü aşağıda tanımlanan ekstra değişkenlerle ( where) öne çıkarmak isteyip istemediğiniz veya her şeyi önceden tanımlayıp formülü koymak isteyip istemediğiniz konusunda kavramsal bir fark da vardır. aşağıda ( let). Her stilin farklı bir vurgusu vardır ve her ikisinin de matematik kağıtlarında, ders kitaplarında vb. Kullanıldığını görürsünüz. Genel olarak, formülün onlar olmadan bir anlam ifade etmeyecek kadar sezgisel olmayan değişkenler yukarıda tanımlanmalıdır; Bağlamdan dolayı sezgisel olan değişkenler veya isimleri aşağıda tanımlanmalıdır. Örneğin, ephemient'in hasVowel örneğinde, anlamı vowelsaçıktır ve bu nedenle kullanımının üzerinde tanımlanmasına gerek yoktur ( letkoruma nedeniyle çalışmayacağı gerçeğini göz ardı ederek ).


1
Bu, iyi bir pratik kural sağlar. Neden izin vermenin kapsamın nerede olduğundan farklı göründüğünü açıklar mısınız?

Çünkü Haskell sözdizimi öyle söylüyor. Üzgünüm, iyi bir cevabım yok. Belki de üst kapsamlı tanımları bir "izin" altına sıkıştırıldığında okumak zordur, bu yüzden izin verilmemiştir.
gdj

13

Yasal:

main = print (1 + (let i = 10 in 2 * i + 1))

Yasal değil:

main = print (1 + (2 * i + 1 where i = 10))

Yasal:

hasVowel [] = False
hasVowel (x:xs)
  | x `elem` vowels = True
  | otherwise = False
  where vowels = "AEIOUaeiou"

Yasal değil: (ML'den farklı olarak)

let vowels = "AEIOUaeiou"
in hasVowel = ...

13
Aşağıdaki örneklerin neden geçerli olduğunu ve diğerinin olmadığını açıklayabilir misiniz?

2
hasVowel = let^M vowels = "AEIOUaeiou"^M in ...^M
Bilmeyenler için

5

LYHFGG'deki bu örneği yararlı buldum :

ghci> 4 * (let a = 9 in a + 1) + 2  
42  

letbir ifadedir, böylece ifadelerin gidebileceği let herhangi bir yere (!) koyabilirsiniz .

Başka bir deyişle, yukarıdaki örnekte , basitçe değiştirmek için kullanmak mümkün değildir (belki de daha ayrıntılı ifadeler ile birlikte kullanılmadan ).whereletcasewhere


3

Ne yazık ki, buradaki cevapların çoğu yeni başlayanlar için fazla teknik.

LHYFGG'nin bununla ilgili bir bölümü vardır - henüz okumadıysanız okumanız gerekir, ancak özünde:

  • wheresadece işlev tanımlarında yararlı olan sözdizimsel bir yapıdır ( şeker değil ) .
  • let ... inbizzat bir ifadedir , bu nedenle onları ifade koyabildiğiniz her yerde kullanabilirsiniz. Kendisi bir ifade olduğundan, gardiyanlar için bir şeyler bağlamak için kullanılamaz.

Son olarak, letliste anlamalarında da kullanabilirsiniz:

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height

Bir yüklemede yaptığımız gibi, bir liste anlayışının içine bir izin ekleriz , sadece listeyi filtrelemez, sadece isimlere bağlanır. Bir liste kavrayışının içindeki let'te tanımlanan adlar, çıktı işlevi ( |öğesinden önceki kısım ) ve bağlamadan sonra gelen tüm tahminler ve bölümler tarafından görülebilir . Böylece işlevimizin yalnızca insanların BMI'lerini> = 25 döndürmesini sağlayabiliriz:


Farkı anlamama gerçekten yardımcı olan tek cevap bu. Teknik cevaplar daha deneyimli bir Haskell-er için faydalı olabilirken, bu benim gibi yeni başlayanlar için yeterince özlü! +1
Zac G
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.