Arasındaki fark nedir . (nokta) ve $ (dolar işareti)?


Yanıtlar:


1229

$Operatör parantez kaçınarak içindir. Ondan sonra görünen her şey, daha önce gelen her şeyden önce gelir.

Örneğin, diyelim ki bir satır var:

putStrLn (show (1 + 1))

Bu parantezlerden kurtulmak istiyorsanız, aşağıdaki satırlardan herhangi biri de aynı şeyi yapar:

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

.Operatörün birincil amacı parantezlerden kaçınmak değil, fonksiyonları zincirlemektir. Sağda görünen her şeyin çıktısını solda görünen her şeyin girişine bağlamanızı sağlar. Bu genellikle daha az parantez ile sonuçlanır, ancak farklı çalışır.

Aynı örneğe dönersek:

putStrLn (show (1 + 1))
  1. (1 + 1)girişi yoktur ve bu nedenle .operatörle birlikte kullanılamaz .
  2. showalabilir Intve geri dönebilir String.
  3. putStrLnalabilir Stringve geri dönebilir IO ().

Sen zinciri olabilir showiçin putStrLnbu gibi:

(putStrLn . show) (1 + 1)

Eğer beğeninize göre çok fazla parantez varsa, $operatörle onlardan kurtulun :

putStrLn . show $ 1 + 1

55
Aslında, + da bir işlev olduğundan, önekini yapamaz ve sonra da `` putStrLn '' gibi yazamazsınız. göstermek . (+) 1 1 `Daha net olmadığı için değil, yani ... yapabilirsin, değil mi?
CodexArcanum

4
@CodexArcanum Bu örnekte, benzer bir şey putStrLn . show . (+1) $ 1eşdeğerdir. Çoğu (tümü?) İnfix operatörünün fonksiyonlar olduğu konusunda haklısınız.
Michael Steele

79
Kimsenin neden bahsetmediğini merak ediyorum map ($3). Yani, çoğunlukla $parantezlerden kaçınmak için kullanıyorum , ama hepsi orada oldukları gibi değil.
Kübik

43
map ($3)bir tür işlevidir Num a => [(a->b)] -> [b]. Bir sayı alan fonksiyonların bir listesini alır, hepsine 3 uygular ve sonuçları toplar.
Kübik

21
Diğer operatörlerle $ kullanırken dikkatli olmalısınız. "x + f (y + z)", "x + f $ y + z" ile aynı değildir, çünkü ikincisi aslında "(x + f) (y + z)" anlamına gelir (yani x ve f'nin toplamı işlev olarak kabul edilir).
Paul Johnson

188

Farklı türleri ve farklı tanımları vardır:

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

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

($)normal işlev uygulamasının yerini almak için tasarlanmıştır ancak parantezlerden kaçınmaya yardımcı olmak için farklı bir önceliğe sahiptir. (.)yeni bir fonksiyon oluşturmak için iki fonksiyonu bir araya getirmek içindir.

Bazı durumlarda değiştirilebilirler, ancak bu genel olarak doğru değildir. Oldukları tipik örnek:

f $ g $ h $ x

==>

f . g . h $ x

Başka bir deyişle, bir $s zincirinde , sonuncusu hariç tümü.


1
xBir işlev olsaydı ne olurdu ? Sonuncusu .olarak kullanabilir misin ?
richizy

3
@richizy Eğer gerçekten xbu bağlamda başvuruyorsanız , o zaman evet - ama sonra "son" olanı başka bir şeye uygulanacaktır x. Başvuru yapmıyorsanız x, xdeğer olmaktan farklı değildir .
GS -

124

Ayrıca unutmayınız ($)olan fonksiyon türlerine uzmanlaşmış kimlik işlevi . Kimlik işlevi şuna benzer:

id :: a -> a
id x = x

Buna rağmen ($)şöyle görünüyor:

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

Tip imzasına bilerek fazladan parantez eklediğimi unutmayın.

Kullanımları ($)genellikle parantez eklenerek ortadan kaldırılabilir (operatör bir bölümde kullanılmadığı sürece). Örn: f $ g xolur f (g x).

Kullanımlarının (.)değiştirilmesi genellikle biraz daha zordur; genellikle bir lambda'ya veya açık bir fonksiyon parametresinin kullanılmasına ihtiyaç duyarlar. Örneğin:

f = g . h

olur

f x = (g . h) x

olur

f x = g (h x)

Bu yardımcı olur umarım!


"Tip imzasına kasıtlı olarak fazladan parantez eklediğimi unutmayın." Kafam karıştı ... bunu neden yaptın?
Mateen Ulhaq

5
@MateenUlhaq ($) türü (a -> b) -> a -> b şeklindedir ve (a -> b) -> (a -> b) ile aynıdır, ancak burada parantez eklenir. berraklık.
Rudi

3
Sanırım. Bunu iki argümanın bir işlevi olarak düşünüyordum ... ama körelme nedeniyle, bir işlevi döndüren bir işleve tam olarak eşdeğer.
Mateen Ulhaq

78

($) değerlendirme sırasını kontrol etmek için parantez eklemeden işlevlerin birlikte zincirlenmesine olanak tanır:

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

Prelude> head $ tail "asdf"
's'

Compose operatörü (.), bağımsız değişkenleri belirtmeden yeni bir işlev oluşturur:

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

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

Yukarıdaki örnek tartışmalı olarak açıklayıcıdır, ancak kompozisyonun kullanım kolaylığını göstermez. İşte başka bir benzetme:

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

Yalnızca bir kez üçüncü kullanırsak, lambda kullanarak adlandırmayı önleyebiliriz:

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

Son olarak, kompozisyon lambdadan kaçınmamızı sağlar:

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

3
Stackoverflow bir kombinasyon işlevi olsaydı, önceki iki açıklama bu cevap örnek ile birleştiren yanıtı tercih ediyorum.
Chris.Q

59

Kısa ve tatlı versiyon:

  • ($) Sağ argüman olan değerde sol argüman olan fonksiyonu çağırır.
  • (.) sol argüman olan fonksiyonu, sağ argüman olan fonksiyon üzerinde oluşturur.

29

Yararlı ve bana bir haskell öğrenmek çok kısa açıklama anlamaya biraz zaman aldı bir uygulama :

f $ x = f x

ve bir infix operatörü içeren bir ifadenin sağ tarafının parantez haline getirilmesi onu bir önek fonksiyonuna dönüştürür, biri buna ($ 3) (4+)benzer şekilde yazabilir (++", world") "hello".

Biri bunu neden yapar ki? Örneğin, işlev listeleri için. Her ikisi de:

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

ve:

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

map (\x -> x ++ ", world") ...veya ' den daha kısadır map (\f -> f 3) .... Açıkçası, ikinci varyantlar çoğu insan için daha okunabilir olacaktır.


14
btw, $3boşluk olmadan kullanmamanızı tavsiye ederim . Şablon Haskell etkinse, bu bir ekleme olarak ayrıştırılır, oysa $ 3her zaman söyledikleriniz anlamına gelir. Genel olarak Haskell'de belirli operatörlerin etraflarında bu şekilde davranılması için boşluklar bulunduğunda ısrar ederek sözdiziminin parçalarını "çalmak" yönünde bir eğilim var gibi görünüyor.
GS - Monica'dan

1
Parantezlerin nasıl çalıştığını anlamak için bir süre aldı: en.wikibooks.org/wiki/Haskell/…
Casebash

19

Haskell: .(nokta) ve $(dolar işareti) arasındaki fark

Nokta (.)ve dolar işareti arasındaki fark nedir ($)? Anladığım kadarıyla, ikisi de parantez kullanmamaları nedeniyle sözdizimsel şekerdir.

Bunlar değil kullanım parantez gerek değil sözdizimsel şeker - onlar fonksiyonları vardır - sabitlendikten, böylece biz operatörleri onları çağırabilir.

Oluşturun (.)ve ne zaman kullanılacağını.

(.)oluşturma işlevidir. Yani

result = (f . g) x

geçirilen argüman sonucunu geçen bir işlevi oluşturmak aynıdır giçin f.

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

Kullan (.)sen oluşturmak isteyen işlevlere geçmek mevcut argümanları olmadığı zamanlarda.

Doğru ilişkilendirme uygulanır ($)ve ne zaman kullanılır

($)düşük bağlanma önceliğine sahip, sağ ilişkisel bir uygulama fonksiyonudur. Bu yüzden önce sadece sağındaki şeyleri hesaplar. Böylece,

result = f $ g x

prosedürle aynıdır (Haskell tembel olarak değerlendirildiğinden önemlidir, önce değerlendirmeye başlayacaktır f):

h = f
g_x = g x
result = h g_x

veya daha kısaca:

result = f (g x)

($)Önceki işlevi sonuca uygulamadan önce değerlendirilecek tüm değişkenlere sahip olduğunuzda kullanın .

Bunu her fonksiyonun kaynağını okuyarak görebiliriz.

Kaynağı Oku

İşte kaynak için (.):

-- | 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)

Ve burada kaynak için ($):

-- | 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

Sonuç

Fonksiyonu hemen değerlendirmeniz gerekmediğinde kompozisyon kullanın. Belki kompozisyondan kaynaklanan fonksiyonu başka bir fonksiyona geçirmek istersiniz.

Tam değerlendirme için tüm argümanları sağlarken uygulamayı kullanın.

Yani örneğimiz için, anlamsal olarak

f $ g x

ne zaman x(ya da daha doğrusu, g'argümanlarımız) varsa ve şunu yaparsak:

f . g

yapmadığımız zaman.


12

Kuralım basit (ben de yeni başlıyorum):

  • kullanmayan .parametresini (function call) geçirmek istiyorsanız ve
  • $henüz parametre yoksa kullanmayın (bir işlev oluşturun)

Yani

show $ head [1, 2]

ama asla:

show . head [1, 2]

3
İyi sezgisel, ancak daha fazla örnek kullanabilir
Zoey Hewll

12

... veya boru hatlarını kullanarak .ve $yapılarından kaçınabilirsiniz :

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

Yardımcı fonksiyona ekledikten sonra:

(|>) x y = y x

2
Evet, |> F # boru hattı işleci.
user1721780

6
Burada dikkat edilmesi gereken bir şey, Haskell'in $operatörünün aslında F # 'dan <|daha fazla çalıştığı |>, tipik olarak haskell'de yukarıdaki işlevi şöyle yazacaksınız: third xs = head $ tail $ tail $ xsya da belki de third = head . tail . tail, F # tarzı sözdiziminde böyle bir şey olurdu:let third = List.head << List.tail << List.tail
Electric Coffee

1
Haskell'i F # gibi göstermek için neden bir yardımcı fonksiyon eklemelisiniz? -1
vikingsteve

10
Ters çevrilmiş $halihazırda mevcuttur ve buna & hackage.haskell.org/package/base-4.8.0.0/docs/…
pat

11

Herhangi bir şey (herhangi bir işlev) hakkında daha fazla bilgi edinmenin harika bir yolu, her şeyin bir işlev olduğunu hatırlamaktır! Bu genel mantra yardımcı olur, ancak operatörler gibi belirli durumlarda, bu küçük numarayı hatırlamaya yardımcı olur:

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

ve

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

Sadece :tliberal olarak kullanmayı ve operatörlerinizi sarmayı unutmayın ()!


0

Bence nerede kullanacağınız .ve $bazı şeyleri açıklığa kavuşturamayacağınıza dair kısa bir örnek .

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

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

times6İşlev kompozisyonundan oluşturulan bir işlev olduğunu unutmayın .


0

Diğer tüm cevaplar oldukça iyi. Ancak ghc'nin $ 'ı nasıl ele aldığı, ghc tipi denetleyicinin daha yüksek rütbe / nicelikli tiplerle instatiaryona izin verdiği konusunda önemli bir kullanılabilirlik detayı vardır. $ idÖrneğin türüne bakarsanız , argümanı kendisi polimorfik bir fonksiyon olan bir fonksiyon alacaktır. Bunun gibi küçük şeyler, eşdeğer bir üzgün operatörle aynı esnekliğe sahip değildir. (Bu aslında $! 'In aynı tedaviyi hak edip etmediğini merak ediyor)


0

$ İle ilgili en önemli kısım, en düşük operatör önceliğine sahip olmasıdır.

Bilgi yazarsanız şunu görürsünüz:

λ> :info ($)
($) :: (a -> b) -> a -> b
    -- Defined in ‘GHC.Base’
infixr 0 $

Bu bize, bunun mümkün olan en düşük önceliğe sahip olan doğru ilişkilendirilebilirliğe sahip bir infix operatörü olduğunu söyler. Normal fonksiyon uygulaması sol ilişkilidir ve en yüksek önceliğe sahiptir (10). Yani $ tam tersi bir şey.

Bu yüzden normal işlev uygulamasının veya () işlevinin çalışmadığı yerlerde kullanırız.

Örneğin, bu işe yarıyor:

λ> head . sort $ "example"
λ> e

ama bu yapmaz:

λ> head . sort "example"

Çünkü . sıralamadan daha düşük önceliğe sahiptir ve (sıralama "örnek") türü [Karakter]

λ> :type (sort "example")
(sort "example") :: [Char]

Fakat . iki işlevi beklemektedir ve sıralama işlemlerinin sırası nedeniyle bunu yapmanın kısa bir yolu yoktur.

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.