Haskell : Where vs. Let


117

저는 Haskell을 처음 접했고 Where vs. Let에 대해 매우 혼란 스럽습니다 . 둘 다 비슷한 목적을 제공하는 것 같습니다. WhereLet 사이의 몇 가지 비교를 읽었 지만 언제 각각을 사용해야하는지 구분하는 데 어려움이 있습니다. 누군가가 컨텍스트를 제공하거나 다른 것을 사용할 때를 보여주는 몇 가지 예를 제공 할 수 있습니까?

어디 대하자

where절은 함수 정의의 수준에서 정의 할 수 있습니다. 일반적으로 let정의 범위와 동일합니다 . 유일한 차이점은 가드를 사용할 때 입니다. where절의 범위는 모든 가드에 걸쳐 있습니다. 반대로 let표현식 의 범위 는 현재 함수 절 및 가드 (있는 경우)뿐입니다.

Haskell 치트 시트

하스켈 위키는 매우 상세하고 다양한 사례를 제공하지만 가상의 예를 사용합니다. 초보자에게는 설명이 너무 짧습니다.

Let의 장점 :

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

Control.Monad.State

where는 f = 일치하는 패턴을 나타내므로 작동하지 않습니다. 여기서 x는 범위에 없습니다. 반대로 let으로 시작했다면 문제가 없을 것입니다.

Let의 장점에 대한 Haskell Wiki

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

Where의 장점 :

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)

선언 대 표현

Haskell wiki는 Where 절이 선언적이지만 Let 표현식은 표현 적이 라고 언급합니다 . 스타일 외에도 어떻게 다르게 수행합니까?

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. 첫 번째 예에서 Let 은 범위에 있지만 어디에 있지 않습니까?
  2. 첫 번째 예에 Where 를 적용 할 수 있습니까 ?
  3. 변수가 실제 표현을 나타내는 실제 예제에 이것을 적용 할 수 있습니까?
  4. 각각을 사용할 때 따라야 할 일반적인 경험 법칙이 있습니까?

최신 정보

나중에이 스레드에서 오는 사람들을 위해 여기에서 찾을 수있는 최상의 설명을 찾았습니다 : " A Gentle Introduction to Haskell ".

식하자.

Haskell의 let 표현식은 중첩 된 바인딩 세트가 필요할 때 유용합니다. 간단한 예로서 다음을 고려하십시오.

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

let 표현식에 의해 생성 된 바인딩 세트는 상호 재귀 적이며 패턴 바인딩은 지연 패턴으로 처리됩니다 (즉, 암시 적 ~을 전달 함). 허용되는 유일한 선언 유형은 형식 서명, 함수 바인딩 및 패턴 바인딩입니다.

Where 절.

때로는 where 절이 필요한 여러 보호 방정식에 대해 바인딩 범위를 지정하는 것이 편리합니다.

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

이 작업은 포함하는 표현식에 대해서만 범위를 지정하는 let 표현식으로는 수행 할 수 없습니다. where 절은 방정식 또는 케이스 표현식 세트의 최상위 레벨에서만 허용됩니다. let 식의 바인딩에 대한 동일한 속성 및 제약 조건이 where 절의 바인딩에 적용됩니다. 이 두 가지 형태의 중첩 범위는 매우 비슷해 보이지만 let 표현식은 표현식 인 반면 where 절은 그렇지 않습니다. 함수 선언 및 케이스 표현식 구문의 일부입니다.


9
나는 Haskell을 처음 배우기 시작했을 때 let와 의 차이에 의아해했습니다 where. 나는 그것을 이해하는 가장 좋은 방법은 둘 사이에 거의 차이가 없기 때문에 걱정할 것이 없다는 것을 깨닫는 것이라고 생각합니다. 매우 간단한 기계적 변형 wherelet통해 주어진 의미 . 참조 haskell.org/onlinereport/decls.html#sect4.4.3.2 이 변환은 표기 편의를 위해 존재하는 정말.
Tom Ellis

일반적으로 먼저 정의하려는 내용에 따라 둘 중 하나를 사용합니다. 예를 들어, 사람들은 종종 함수를 사용하고 그 위치에서 정의합니다. Let은 일종의 명령형 기능을 원할 때 사용됩니다.
PyRulez

@Tom Ellis, Tom, 나는 당신이 언급 한 링크를 이해하려고 노력했지만 나에게는 너무 어려웠습니다.이 간단한 변형을 단순한 인간으로 설명해 주시겠습니까?
jhegedus

1
@jhegedus : f = body where x = xbody; y = ybody ...수단f = let x = xbody; y = ybody ... in body
톰 엘리스

톰 감사합니다! 다른 방향으로 갈 수 있습니까? case .... of ... where어떻게 든 let 식을 식으로 변환 할 수 있습니까? 이것에 대해 잘 모르겠습니다.
jhegedus

답변:


39

1 : 예제의 문제

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

매개 변수 x입니다. where절의 항목은 함수의 매개 변수 f(아무것도 없음)와 외부 범위 의 항목 만 참조 할 수 있습니다 .

2 : where첫 번째 예제에서 a를 사용하려면 다음 x과 같이를 매개 변수로 사용하는 두 번째 명명 된 함수를 도입 할 수 있습니다 .

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

또는 다음과 같이 :

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

3 : 다음은 ...'가 없는 완전한 예입니다 .

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 : 언제 사용 let하거나 where맛의 문제입니다. 나는 let계산을 강조 (앞으로 이동)하고 where프로그램 흐름 (계산을 뒤로 이동 ) 을 강조하기 위해 사용합니다.


2
"where 절에있는 것들은 함수 f (아무것도 없음)의 매개 변수와 외부 범위에있는 것만을 참조 할 수 있습니다." -그것은 나를 위해 그것을 명확히하는 데 정말로 도움이됩니다.

28

ephemient가 지적한 가드와 관련하여 기술적 인 차이가 있지만, 아래에 정의 된 추가 변수 ( where)를 사용하여 기본 공식을 앞에 넣을 것인지 또는 모든 것을 미리 정의하고 공식을 넣을 것인지에 대한 개념적 차이도 있습니다. 아래 ( let). 각 스타일은 서로 다른 강조점을 가지고 있으며 수학 논문, 교과서 등에서 사용되는 두 가지 모두를 볼 수 있습니다. 일반적으로 수식이 의미가 없을 정도로 직관적이지 않은 변수는 위에서 정의해야합니다. 컨텍스트 나 이름으로 인해 직관적 인 변수는 아래에 정의해야합니다. 예를 들어, ephemient의 hasVowel 예제에서의 의미 vowels는 분명하므로 사용법 위에 정의 할 필요가 없습니다 ( let가드로 인해 작동하지 않는 사실은 무시 함 ).


1
이것은 좋은 경험 법칙을 제공합니다. 범위가 어디와 다른 것처럼 보이는 이유에 대해 자세히 설명해 주시겠습니까?

Haskell 구문이 그렇게 말하고 있기 때문입니다. 죄송합니다. 좋은 답변이 없습니다. 최상위 범위 정의는 "let"아래에 넣어서 읽기가 어렵 기 때문에 허용되지 않을 수 있습니다.
gdj

13

적법한:

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

합법적이지 않음 :

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

적법한:

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

합법적이지 않음 : (ML과 다름)

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

13
다음 예는 유효하지만 다른 예는 유효하지 않은 이유를 설명 할 수 있습니까?

2
모르는 사람들을 위해 이것은 합법적입니다 : hasVowel = let^M vowels = "AEIOUaeiou"^M in ...( ^Mis newline)
Thomas Eding 2014-04-24

5

LYHFGG 의이 예제가 도움 이된다는 것을 알았습니다 .

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

let은 표현식이므로 표현식이 갈 수있는 let 곳에 (!)를 넣을 수 있습니다.

즉, 위의 예 에서 단순히 대체하는 데 사용할 수 없습니다 ( 와 결합 된 더 자세한 표현을 사용하지 않고 ).whereletcasewhere


3

슬프게도 여기에있는 대부분의 답변은 초보자에게는 너무 기술적입니다.

LHYFGG 에는 관련 장이 있습니다. 아직 읽지 않았다면 읽어야하지만 본질적으로 :

  • where함수 정의에서만 유용한 구문 구조 ( 설탕 아님)입니다 .
  • let ... in표현식 자체 이므로 표현식을 넣을 수있는 모든 곳에서 사용할 수 있습니다. 표현 그 자체이기 때문에 경비원을 위해 물건을 묶는 데 사용할 수 없습니다.

마지막으로 let목록 내포에서도 사용할 수 있습니다 .

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

우리는 술어처럼 목록 이해력 안에 let을 포함 합니다. 단지 목록을 필터링하지 않고 이름에만 바인딩합니다. 목록 이해 내에서 let에 정의 된 이름은 출력 함수 (앞 부분 |) 및 바인딩 뒤에 오는 모든 술어 및 섹션에 표시됩니다. 따라서 함수가> = 25 인 사람의 BMI 만 반환하도록 만들 수 있습니다.


이것이 실제로 차이점을 이해하는 데 도움이 된 유일한 대답입니다. 기술적 인 답변은 더 숙련 된 Haskell-er에게 유용 할 수 있지만, 저 같은 초보자에게는 충분히 간결합니다! +1
Zac G
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.