Haskell의 도트 연산자 : 더 많은 설명이 필요합니다.


86

이 Haskell 코드에서 도트 연산자가 수행하는 작업을 이해하려고합니다.

sumEuler = sum . (map euler) . mkList

전체 소스 코드는 다음과 같습니다.

나의 이해

점 연산자는 두 가지 함수 sum와 결과 map euler및 결과를 mkList입력으로 취합니다 .

하지만 sum함수가 아니라 함수의 인수 인 거죠? 그래서 여기서 무슨 일이 일어나고 있습니까?

또한 무엇을 (map euler)하고 있습니까?

암호

mkList :: Int -> [Int]
mkList n = [1..n-1]

euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))

sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList

답변:


138

간단히 말해서 .수학에서와 같이 함수 구성입니다.

f (g x) = (f . g) x

귀하의 경우 sumEuler다음과 같이 정의 할 수 있는 새 함수를 만들고 있습니다.

sumEuler x = sum (map euler (mkList x))

예제의 스타일은 "점없는"스타일이라고합니다. 함수에 대한 인수는 생략됩니다. 이것은 많은 경우에 더 명확한 코드를 만듭니다. (처음 보는 것은 어렵지만 잠시 후에 익숙해 질 것입니다. 일반적인 Haskell 관용구입니다.)

여전히 혼란 스러우면 .UNIX 파이프와 같은 것과 관련된 것이 도움이 될 수 있습니다 . 경우 f의 출력이되고 g출력이됩니다의 입력 ' h의 입력을, 당신은 같은 명령 행에 그 쓰기 것 f < x | g | h. Haskell에서는 .UNIX처럼 작동 |하지만 "뒤로"- h . g . f $ x. 이 표기법은 목록을 처리 할 때 매우 유용합니다. 과 같은 다루기 힘든 구조 대신 map (\x -> x * 2 + 10) [1..10]그냥 쓸 수 있습니다 (+10) . (*2) <$> [1..10]. (그리고 해당 함수를 단일 값에만 적용하려면 (+10) . (*2) $ 10. Consistent입니다!)

Haskell 위키에는 좀 더 자세한 내용이 담긴 좋은 기사가 있습니다 : http://www.haskell.org/haskellwiki/Pointfree


1
작은 문제 : 첫 번째 코드 조각은 실제로 유효한 Haskell이 아닙니다.
SwiftsNamesake

2
@SwiftsNamesake Haskell에 능통하지 않은 사람들에게 단일 등호 기호가 여기서 의미가 없다는 것을 의미합니까? (스 니펫은 " f (g x)= (f . g) x"?) 형식이어야합니다. 아니면 다른 것이 있습니까?
user234461 jul.

1
@ user234461 맞아요. ==유효한 표준 Haskell을 원한다면 대신 필요 합니다.
SwiftsNamesake

그 작은 스 니펫 위쪽은 금색입니다. 여기의 다른 답변과 마찬가지로 정확하지만 그 스 니펫은 내 머리에서 직관적으로 직접 클릭하여 나머지 답변을 읽을 필요가 없었습니다.
Tarick Welling

24

. 연산자는 함수를 구성합니다. 예를 들면

a . b

어디 와 b를 함수는 새로운되어있는 기능 이 다음 인수, b를 실행하는 결과에. 귀하의 코드

sumEuler = sum . (map euler) . mkList

다음과 정확히 동일합니다.

sumEuler myArgument = sum (map euler (mkList myArgument))

하지만 읽기가 더 쉬웠 으면합니다. map euler 주위에 괄호가있는 이유는 sum , map eulermkList - map euler 가 단일 함수 라는 세 가지 함수가 구성되어 있음을 더 명확하게하기 때문 입니다.


23

sum에 대한 인수가 아니라 Haskell Prelude의 함수입니다 sumEuler. 유형이 있습니다

Num a => [a] -> a

함수 구성 연산자 . 에는 유형이 있습니다.

(b -> c) -> (a -> b) -> a -> c

그래서 우리는

           euler           ::  Int -> Int
       map                 :: (a   -> b  ) -> [a  ] -> [b  ]
      (map euler)          ::                 [Int] -> [Int]
                    mkList ::          Int -> [Int]
      (map euler) . mkList ::          Int ->          [Int]
sum                        :: Num a =>                 [a  ] -> a
sum . (map euler) . mkList ::          Int ->                   Int

참고 Int참의 인스턴스입니다 Numtypeclass.


11

. 연산자는 함수 구성에 사용됩니다. 수학과 마찬가지로 f (x) 및 g (x) f 함수가 필요한 경우. g는 f (g (x))가됩니다.

map은 목록에 함수를 적용하는 내장 함수입니다. 함수를 괄호 안에 넣으면 함수가 인수로 처리됩니다. 이것에 대한 용어는 카레 입니다. 당신은 그것을 찾아야합니다.

하는 것은 두 개의 인수가있는 함수를 취하고 인수 오일러를 적용한다는 것입니다. (맵 오일러) 맞죠? 결과는 하나의 인수 만 취하는 새 함수입니다.

합계. (지도 오일러). mkList는 기본적으로이 모든 것을 합치는 멋진 방법입니다. 내 Haskell은 약간 녹슬었지만 마지막 기능을 직접 조합 할 수 있습니까?


5

Haskell의 점 연산자

이 Haskell 코드에서 도트 연산자가 수행하는 작업을 이해하려고합니다.

sumEuler = sum . (map euler) . mkList

짧은 답변

점이없는 등가 코드, 즉

sumEuler = \x -> sum ((map euler) (mkList x))

또는 람다없이

sumEuler x = sum ((map euler) (mkList x))

점 (.)은 기능 구성을 나타냅니다.

더 긴 답변

먼저 eulerto 의 부분적 적용을 단순화합시다 map.

map_euler = map euler
sumEuler = sum . map_euler . mkList

이제 우리는 점만 가지고 있습니다. 이 점은 무엇을 나타 냅니까?

에서 소스 :

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

따라서 (.)는 IS 작성 연산자 .

짓다

수학에서 함수 f (x)와 g (x), 즉 f (g (x))의 구성을 다음과 같이 쓸 수 있습니다.

(f ∘ g) (x)

"g로 구성된 f"로 읽을 수 있습니다.

따라서 Haskell에서 f ∘ g 또는 g로 구성된 f는 다음과 같이 쓸 수 있습니다.

f . g

컴포지션은 연관성이 있습니다. 즉, 컴포지션 연산자로 작성된 f (g (h (x)))는 모호함없이 괄호를 생략 할 수 있습니다.

즉, (f ∘ g) ∘ h는 f ∘ (g ∘ h)와 동일하므로 간단히 f ∘ g ∘ h로 쓸 수 있습니다.

뒤로 돌기

이전 단순화로 돌아 가면 다음과 같습니다.

sumEuler = sum . map_euler . mkList

이는 sumEuler해당 기능의 적용되지 않은 구성 임을 의미합니다 .

sumEuler = \x -> sum (map_euler (mkList x))

4

도트 연산자는 왼쪽에있는 함수 ( sum)를 오른쪽에있는 함수의 출력에 적용합니다. 귀하의 경우에는, 당신은 함께 여러 가지 기능을 체인있어 - 당신의 결과를 전달하는 mkList(map euler), 다음에 그 결과를 전달합니다 sum. 이 사이트 에는 몇 가지 개념에 대한 좋은 소개가 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.