의 차이점은 무엇입니까? (점)과 $ (달러 기호)?


709

(.)과 달러 기호 의 차이점은 무엇입니까 ($)?

내가 이해하는 것처럼, 그들은 괄호를 사용할 필요가 없기 때문에 둘 다 구문 설탕입니다.

답변:


1226

$연산자는 괄호를 회피하기위한 것이다. 그 후에 나타나는 것은 앞에 오는 것보다 우선합니다.

예를 들어 다음과 같은 줄이 있다고 가정 해 봅시다.

putStrLn (show (1 + 1))

이러한 괄호를 제거하려면 다음 행 중 하나도 동일한 작업을 수행합니다.

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

.연산자 의 주요 목적은 괄호를 피하는 것이 아니라 함수를 연결하는 것입니다. 오른쪽에 나타나는 모든 출력을 왼쪽에 나타나는 모든 입력에 연결할 수 있습니다. 일반적으로 괄호가 적지 만 다르게 작동합니다.

같은 예로 돌아가서 :

putStrLn (show (1 + 1))
  1. (1 + 1)입력이 없으므로 .연산자 와 함께 사용할 수 없습니다 .
  2. show를 가져 와서 a Int를 반환 할 수 있습니다 String.
  3. putStrLn를 가져 String와서을 반환 할 수 있습니다 IO ().

다음 과 같이 체인 show을 연결할 수 있습니다 putStrLn.

(putStrLn . show) (1 + 1)

원하는 괄호가 너무 많으면 $연산자를 사용하여 제거하십시오 .

putStrLn . show $ 1 + 1

54
실제로 +도 함수이기 때문에`putStrLn과 같이 접두사를 만들어서 작성할 수도 없습니다. 보여 주다 . (+) 1 1`더 명확하지는 않지만, 당신은 할 수 있습니다. 맞습니까?
CodexArcanum

4
@CodexArcanum이 예제에서와 같은 putStrLn . show . (+1) $ 1것이 같습니다. 대부분의 (모두?) 접두사 연산자가 함수라는 것이 맞습니다.
Michael Steele

79
왜 아무도 같은 용도로 언급하지 않았는지 궁금합니다 map ($3). 내 말은, 주로 $괄호를 피하기 위해 사용 하지만 그것이 전부는 아닙니다.
큐빅

43
map ($3)유형의 함수입니다 Num a => [(a->b)] -> [b]. 숫자를 취하는 함수 목록을 가져 와서 3 개를 모두 적용하고 결과를 수집합니다.
큐빅

21
다른 연산자와 함께 $를 사용할 때는주의해야합니다. "x + f (y + z)"는 "x + f $ y + z"와 동일하지 않습니다. "x + f (y + z)"는 실제로 "(x + f) (y + z)"를 의미하기 때문에 (x와 f의 합은 기능으로 취급).
Paul Johnson

186

유형과 정의가 다릅니다.

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

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x

($)괄호를 피하기 위해 일반적인 함수 응용 프로그램을 대체하지만 다른 우선 순위를 갖습니다. (.)새로운 기능을 만들기 위해 두 개의 기능을 함께 구성하기위한 것입니다.

어떤 경우에는 상호 교환이 가능하지만 일반적으로 사실이 아닙니다. 이들이있는 전형적인 예는 다음과 같습니다.

f $ g $ h $ x

==>

f . g . h $ x

다시 말해서 $s 체인에서 , 마지막 체인을 제외한 모든 체인은.


1
x함수 라면 어떨까요? 그런 다음 .마지막으로 사용할 수 있습니까 ?
richizy

3
@richizy 만약 당신 x이이 맥락에서 실제로 적용한다면 , 그렇습니다. 그러나 "최종"은 다른 것보다 다른 것에 적용될 것 x입니다. 을 적용하지 않으면 값 x과 다르지 않습니다 x.
GS-Monica에게 사과 함

124

또한주의 ($)이다 기능 유형에 전문 신원 기능 . identity 함수는 다음과 같습니다.

id :: a -> a
id x = x

반면 ($)다음과 같다 :

($) :: (a -> b) -> (a -> b)
($) = id

타입 시그니처에 의도적으로 여분의 괄호를 추가했습니다.

($)연산자는 섹션에서 사용되지 않는 한 괄호를 추가하여 일반적으로 사용을 제거 할 수 있습니다. 예 : f $ g x됩니다 f (g x).

의 사용은 (.)대체하기가 약간 더 어렵다. 그들은 일반적으로 람다 또는 명시 적 함수 매개 변수의 도입이 필요합니다. 예를 들면 다음과 같습니다.

f = g . h

된다

f x = (g . h) x

된다

f x = g (h x)

도움이 되었기를 바랍니다!


"유형 서명에 의도적으로 여분의 괄호를 추가했습니다." 혼란 스러워요. 왜 이러셨어요?
Mateen Ulhaq 2016 년

3
@MateenUlhaq ($)의 유형은 (a-> b)-> a-> b이며 (a-> b)-> (a-> b)와 동일하지만 여분의 괄호는 여기에 명쾌함.
Rudi

2
아. 나는 그것을 두 가지 인수의 함수로 생각했지만 카레 때문에 함수를 반환하는 함수와 정확히 동일합니다.
Mateen Ulhaq

78

($) 평가 순서를 제어하기 위해 괄호를 추가하지 않고 함수를 함께 연결할 수 있습니다.

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'

compose 연산자 (.)는 인수를 지정하지 않고 새 함수를 작성합니다.

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'

위의 예는 논쟁의 여지가 있지만 실제로는 컴포지션 사용의 편의성을 보여주지는 않습니다. 여기 또 다른 비유가 있습니다.

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"

세 번째 만 한 번만 사용하면 람다를 사용하여 이름을 피할 수 있습니다.

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

마지막으로 구성은 람다를 피할 수 있습니다.

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

3
stackoverflow에 조합 기능이있는 경우 이전 두 설명을이 답변의 예와 결합하는 답변을 선호합니다.
Chris.Q

59

짧고 달콤한 버전 :

  • ($) 오른쪽 인수 인 값에서 왼쪽 인수 인 함수를 호출합니다.
  • (.) 오른쪽 인수 인 함수에서 왼쪽 인수 인 함수를 구성합니다.

29

유용하고 매우 짧은 설명 에서 알아 내는 데 시간이 걸리는 하나의 응용 프로그램은 haskell을 배우십시오 .

f $ x = f x

그리고 infix 연산자를 포함하는 표현식의 오른쪽을 괄호로 묶으면 접두사 함수로 변환 ($ 3) (4+)됩니다 (++", world") "hello".

왜 이런 짓을했을까요? 예를 들어 기능 목록. 양자 모두:

map (++", world") ["hello","goodbye"]`

과:

map ($ 3) [(4+),(3*)]

map (\x -> x ++ ", world") ...또는 보다 짧습니다 map (\f -> f 3) .... 분명히 후자의 변형은 대부분의 사람들이 더 읽기 쉽습니다.


14
btw, 나는 $3공백없이 사용 하지 않는 것이 좋습니다. Template Haskell이 활성화 된 경우, 이것은 스플 라이스로 구문 분석되지만 $ 3항상 말한 것을 의미합니다. 일반적으로 Haskell에서는 특정 연산자에 주위에 공백이 있다고 주장함으로써 구문의 일부를 "훔쳐"버리는 경향이있는 것으로 보입니다.
GS-

1
: 괄호가 작업하는 방법을 알아낼 걸 렸어요 en.wikibooks.org/wiki/Haskell/...
Casebash

18

Haskell : .(점)과 $(달러 기호)의 차이점

(.)과 달러 기호 의 차이점은 무엇입니까 ($)? 내가 이해하는 것처럼, 그들은 괄호를 사용할 필요가 없기 때문에 둘 다 구문 설탕입니다.

그것들은 괄호를 사용할 필요 가 없어서 구문 설탕이 아닙니다-그것들은 함수이며, 따라서 우리는 그들을 연산자라고 부를 수 있습니다.

작성 (.)및 사용시기.

(.)작성 기능입니다. 그래서

result = (f . g) x

인수로 전달 결과 전달 함수 구축과 동일 g행 온 f.

h = \x -> f (g x)
result = h x

(.)작성하려는 함수에 전달할 인수가없는 경우에 사용하십시오 .

올바른 연관 적용 ($)및 사용시기

($)바인딩 우선 순위가 낮은 오른쪽 연관 적용 함수입니다. 따라서 단지 오른쪽에있는 것을 먼저 계산합니다. 그러므로,

result = f $ g x

절차 상으로 이것과 동일합니다 (Haskell이 게으르게 평가되기 때문에 중요합니다 f.)

h = f
g_x = g x
result = h g_x

더 간결하게 :

result = f (g x)

($)선행 함수를 결과에 적용하기 전에 평가할 모든 변수가있을 때 사용하십시오 .

우리는 각 기능의 소스를 읽음으로써 이것을 볼 수 있습니다.

소스를 읽으십시오

소스 는 다음과 같습니다 (.).

-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

그리고 여기에 대한 소스 가 있습니다 ($):

-- | Application operator.  This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- >     f $ g $ h x  =  f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

결론

기능을 즉시 평가할 필요가없는 경우 구성을 사용하십시오. 컴포지션에서 생성 된 함수를 다른 함수로 전달하려고 할 수 있습니다.

전체 평가를 위해 모든 인수를 제공 할 때 응용 프로그램을 사용하십시오.

예를 들어, 의미 적으로는

f $ g x

우리가 x(또는 오히려 g논쟁을 할 때 ), 그리고 할 때 :

f . g

우리가하지 않을 때.


12

... 또는 파이프 라이닝 을 사용하여 .$구성을 피할 수 있습니다 .

third xs = xs |> tail |> tail |> head

헬퍼 함수에 추가 한 후 :

(|>) x y = y x

2
예, |>는 F # 파이프 라인 연산자입니다.
user1721780

6
여기서주의 할 점은, 하스켈의 것입니다 $운영자가 실제로 '더 F 번호 같은 작품이야 <|그것보다 |>일반적으로 하스켈에서이 같은 위의 함수를 써서 : third xs = head $ tail $ tail $ xs또는 심지어 같은 third = head . tail . tailF에 # 스타일의 구문이 같을 것이다 :let third = List.head << List.tail << List.tail
전기 커피

1
Haskell을 F #처럼 보이도록 헬퍼 함수를 ​​추가해야하는 이유는 무엇입니까? -1
vikingsteve

9
뒤집힌 부분 $은 이미 사용 가능하며 & hackage.haskell.org/package/base-4.8.0.0/docs/…
pat pat

11

무엇이든 (모든 기능)에 대해 더 많이 배울 수있는 좋은 방법은 모든 것이 기능이라는 것을 기억하는 것입니다! 일반적인 만트라는 도움이되지만 연산자와 같은 특정 경우에는이 작은 트릭을 기억하는 데 도움이됩니다.

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

:t ($)
($) :: (a -> b) -> a -> b

:t자유로이 사용 하고 연산자를 래핑하십시오 ()!


11

내 규칙은 간단합니다 (저도 초보자입니다).

  • .매개 변수를 전달하려면 (함수 호출) 사용하지 마십시오.
  • $아직 매개 변수가 없으면 사용하지 마십시오 (함수 구성)

그건

show $ head [1, 2]

그러나 결코 :

show . head [1, 2]

3
휴리스틱은 훌륭하지만 더 많은 예제를 사용할 수 있습니다.
Zoey Hewll

0

나는 당신이 어디를 사용 .하고 $명확하게하는 데 도움이 되지 않는 짧은 예를 생각합니다 .

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

참고 times6함수 조성물로부터 생성하는 함수이다.


0

다른 모든 대답은 꽤 좋습니다. 그러나 ghc가 $를 처리하는 방법에 대한 중요한 유용성 세부 정보가 있습니다. ghc 유형 검사기는 더 높은 순위 / 수량화 된 유형으로 instatiarion을 허용합니다. $ id예를 들어 유형을 살펴보면 인수 자체가 다형성 함수 인 함수를 사용하게됩니다. 그와 같은 작은 것들에는 동등한 화가 연산자와 동일한 유연성이 부여되지 않습니다. (이것은 실제로 $!가 같은 치료를받을 가치가 있는지 궁금해합니다)

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