기본 do 블록 구문에 대한 설명 필요
ghci에서는 다음과 같이 썼습니다.
let x = do
i <- [1..5]
j <- [2..4]
return i
예상 결과:
[1,2,3,4,5]
실제 결과:
[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
나는 그 출력의 논리를 이해하지 못합니다. 그 이유는 모나드에 관한 것일 수도 있지만, 저는 함수형 프로그래밍에 매우 익숙하지 않습니다. 누군가가 그것을 조금 설명해 주었으면합니다.
나는 또한 List-comprehension에서 동등한 형태를 시도해 보았고 결과는 동일합니다. 즉, 여기에 내가 오해 한 기본적인 것이 있다는 것을 의미합니다.
답변
나는 또한 List-comprehension에서 동등한 형태를 시도했으며 결과는 동일합니다.
좋은 생각. 따라서 목록의 경우 do
표기법은 목록 이해력과 정확히 동일합니다. (사실, 모든 모나드에 대해 표기법을 사용할 수있는 것처럼 모든 모나드에 대해 목록 이해 표기법을 사용할 수 있는 구문 확장 이 있습니다 do
.)
왜 그래서, 당신은 요구하고 [a | a<-[0,1], b<-[2,3]]
있습니다 [0,0,1,1]
대신 [0,1]
. 이것이 놀랍게 보이는 방식 은 수학에서 찾을 수있는 것과 같은 집합 이해력 으로 목록 이해력을 생각하는 경우 입니다. 그러나 목록은 집합이 아니지만 Haskeller는 목록을 집합에 대한 임시 수단으로 사용하는 경우가 많습니다. 목록 이해력이 집합 이해력으로 작용했다면
[x | x <- [0,1,0]]
또한 [0,1]
결과 로만 산출해야합니다 (또는 적어도 동일한 결과를 산출해야합니다 [x|x<-[0,1]]
).
일반적으로 이러한 종류의 중복 제거에는 동등성 검사가 필요하며 효율적으로 만들려면 순서 지정 또는 해싱 방법도 필요합니다. 리스트는 그런 일을하지 않으므로 세트와 같은 동작을 원한다면 세트 구현 데이터 구조를 사용해야합니다. Set그리고 HashSet가장 일반적이다.
이것은 do 메커니즘이 (다행히도) 가장 안쪽의 코드가 실제로 루프 변수를 참조하는지 여부에 대해 신경 쓰지 않기 때문입니다.
가장 안쪽 코드에 관계없이 항상 3 * 5 = 15 값을 얻습니다.
λ>
λ> xs1 = do { i <- [1..5] ; j <- [2..4] ; return i }
λ> xs1
[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
λ>
λ> xs2 = do { i <- [1..5] ; j <- [2..4] ; return 9 }
λ> xs2
[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9]
λ>
λ> xs3 = do { i <- [1..5] ; j <- [2..4] ; return (i,j) }
λ> xs3
[(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(3,2),(3,3),(3,4),(4,2),(4,3),(4,4),(5,2),(5,3),(5,4)]
λ>
λ> length xs1
15
λ> length xs2
15
λ> length xs3
15
λ>
내가 말할 수있는 한, 이것은 Haskell이 C, C ++, Fortran, Python과 공유하는 완벽한 표준 동작입니다.
C ++에 해당하는 예 :
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vi{1,2,3,4,5};
std::vector<int> vj{2,3,4};
for (int i: vi)
for (int j: vj)
std::cout << i << ", ";
std::cout << std::endl;
return EXIT_SUCCESS;
}
C ++ 출력 :
$ ./a.out 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, $