Nokta (.)
ve dolar işareti arasındaki fark nedir ($)
?
Anladığım kadarıyla, ikisi de parantez kullanmamaları nedeniyle sözdizimsel şekerdir.
Nokta (.)
ve dolar işareti arasındaki fark nedir ($)
?
Anladığım kadarıyla, ikisi de parantez kullanmamaları nedeniyle sözdizimsel şekerdir.
Yanıtlar:
$
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)
girişi yoktur ve bu nedenle .
operatörle birlikte kullanılamaz .show
alabilir Int
ve geri dönebilir String
.putStrLn
alabilir String
ve geri dönebilir IO ()
.Sen zinciri olabilir show
için putStrLn
bu gibi:
(putStrLn . show) (1 + 1)
Eğer beğeninize göre çok fazla parantez varsa, $
operatörle onlardan kurtulun :
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1
eşdeğerdir. Çoğu (tümü?) İnfix operatörünün fonksiyonlar olduğu konusunda haklısınız.
map ($3)
. Yani, çoğunlukla $
parantezlerden kaçınmak için kullanıyorum , ama hepsi orada oldukları gibi değil.
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.
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ü.
x
Bir işlev olsaydı ne olurdu ? Sonuncusu .
olarak kullanabilir misin ?
x
bu 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
, x
değer olmaktan farklı değildir .
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 x
olur 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!
($)
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"
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.
$3
boşluk olmadan kullanmamanızı tavsiye ederim . Şablon Haskell etkinse, bu bir ekleme olarak ayrıştırılır, oysa $ 3
her 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.
Haskell:
.
(nokta) ve$
(dolar işareti) arasındaki farkNokta
(.)
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.
(.)
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 g
için f
.
h = \x -> f (g x)
result = h x
Kullan (.)
sen oluşturmak isteyen işlevlere geçmek mevcut argümanları olmadığı zamanlarda.
($)
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.
İş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
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.
Kuralım basit (ben de yeni başlıyorum):
.
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]
... 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
$
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 $ xs
ya da belki de third = head . tail . tail
, F # tarzı sözdiziminde böyle bir şey olurdu:let third = List.head << List.tail << List.tail
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 :t
liberal olarak kullanmayı ve operatörlerinizi sarmayı unutmayın ()
!
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 .
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)
$ İ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.