Apa perbedaan antara titik (.)
dan tanda dolar ($)
?
Seperti yang saya pahami, keduanya adalah gula sintaksis karena tidak perlu menggunakan tanda kurung.
Apa perbedaan antara titik (.)
dan tanda dolar ($)
?
Seperti yang saya pahami, keduanya adalah gula sintaksis karena tidak perlu menggunakan tanda kurung.
Jawaban:
The $
operator adalah untuk menghindari tanda kurung. Apa pun yang muncul setelah itu akan diutamakan daripada apa pun yang datang sebelumnya.
Misalnya, Anda memiliki garis yang berbunyi:
putStrLn (show (1 + 1))
Jika Anda ingin menghilangkan tanda kurung itu, salah satu dari baris berikut ini juga akan melakukan hal yang sama:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
Tujuan utama .
operator bukan untuk menghindari tanda kurung, tetapi untuk fungsi rantai. Ini memungkinkan Anda mengikat output apa pun yang muncul di sebelah kanan dengan input apa pun yang muncul di sebelah kiri. Ini biasanya juga menghasilkan lebih sedikit kurung, tetapi bekerja secara berbeda.
Kembali ke contoh yang sama:
putStrLn (show (1 + 1))
(1 + 1)
tidak memiliki input, dan karena itu tidak dapat digunakan dengan .
operator.show
dapat mengambil Int
dan mengembalikan a String
.putStrLn
dapat mengambil String
dan mengembalikan sebuah IO ()
.Anda dapat rantai show
untuk putStrLn
seperti ini:
(putStrLn . show) (1 + 1)
Jika terlalu banyak tanda kurung untuk keinginan Anda, singkirkan mereka dengan $
operator:
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1
akan setara. Anda benar karena sebagian besar (semua?) Operator infiks adalah fungsi.
map ($3)
. Maksudku, aku lebih sering menggunakannya $
untuk menghindari tanda kurung, tapi bukan hanya itu yang ada di sana.
map ($3)
adalah fungsi dari tipe Num a => [(a->b)] -> [b]
. Dibutuhkan daftar fungsi mengambil nomor, berlaku 3 untuk semuanya dan mengumpulkan hasilnya.
Mereka memiliki tipe dan definisi yang berbeda:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)
dimaksudkan untuk menggantikan aplikasi fungsi normal tetapi pada prioritas yang berbeda untuk membantu menghindari tanda kurung. (.)
adalah untuk menyusun dua fungsi bersama untuk membuat fungsi baru.
Dalam beberapa kasus mereka dapat dipertukarkan, tetapi ini tidak benar secara umum. Contoh khas di mana mereka berada adalah:
f $ g $ h $ x
==>
f . g . h $ x
Dengan kata lain dalam rantai $
s, semua kecuali yang terakhir dapat digantikan oleh.
x
suatu fungsi? Bisakah Anda gunakan .
sebagai yang terakhir?
x
dalam konteks ini, maka ya - tapi kemudian yang "final" akan berlaku untuk sesuatu selain x
. Jika Anda tidak mendaftar x
, maka tidak ada bedanya dengan x
menjadi nilai.
Juga mencatat bahwa ($)
adalah fungsi identitas khusus untuk jenis fungsi . Fungsi identitas terlihat seperti ini:
id :: a -> a
id x = x
Sementara ($)
terlihat seperti ini:
($) :: (a -> b) -> (a -> b)
($) = id
Perhatikan bahwa saya sengaja menambahkan tanda kurung tambahan pada tanda tangan jenis.
Penggunaan ($)
biasanya dapat dihilangkan dengan menambahkan tanda kurung (kecuali operator digunakan dalam bagian). Misalnya: f $ g x
menjadi f (g x)
.
Penggunaan (.)
sering sedikit lebih sulit untuk diganti; mereka biasanya membutuhkan lambda atau pengenalan parameter fungsi eksplisit. Sebagai contoh:
f = g . h
menjadi
f x = (g . h) x
menjadi
f x = g (h x)
Semoga ini membantu!
($)
memungkinkan fungsi dirantai bersama tanpa menambahkan tanda kurung untuk mengontrol urutan evaluasi:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
Operator penulisan (.)
membuat fungsi baru tanpa menentukan argumen:
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
Contoh di atas bisa dibilang ilustratif, tetapi tidak benar-benar menunjukkan kenyamanan menggunakan komposisi. Berikut analogi lainnya:
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
Jika kita hanya menggunakan yang ketiga sekali, kita dapat menghindari memberi nama dengan menggunakan lambda:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
Akhirnya, komposisi memungkinkan kita menghindari lambda:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
Salah satu aplikasi yang berguna dan perlu waktu bagi saya untuk mencari tahu dari deskripsi yang sangat singkat di belajar Anda haskell : Sejak:
f $ x = f x
dan tanda kurung sisi kanan ekspresi yang mengandung operator infiks mengubahnya menjadi fungsi awalan, orang dapat menulis ($ 3) (4+)
analog dengan (++", world") "hello"
.
Mengapa ada orang yang melakukan hal ini? Untuk daftar fungsi, misalnya. Kedua:
map (++", world") ["hello","goodbye"]`
dan:
map ($ 3) [(4+),(3*)]
lebih pendek dari map (\x -> x ++ ", world") ...
atau map (\f -> f 3) ...
. Jelas, varian yang terakhir akan lebih mudah dibaca oleh kebanyakan orang.
$3
ruang. Jika Template Haskell diaktifkan, ini akan diuraikan sebagai sambungan, sedangkan $ 3
selalu berarti apa yang Anda katakan. Secara umum tampaknya ada kecenderungan di Haskell untuk "mencuri" sintaks dengan bersikeras bahwa operator tertentu memiliki ruang di sekitar mereka untuk diperlakukan seperti itu.
Haskell: perbedaan antara
.
(titik) dan$
(tanda dolar)Apa perbedaan antara titik
(.)
dan tanda dolar($)
? Seperti yang saya pahami, keduanya adalah gula sintaksis karena tidak perlu menggunakan tanda kurung.
Mereka bukan gula sintaksis karena tidak perlu menggunakan tanda kurung - mereka adalah fungsi, - diinfiksasi, sehingga kita dapat menyebutnya operator.
(.)
dan kapan menggunakannya.(.)
adalah fungsi menulis. Begitu
result = (f . g) x
sama dengan membangun fungsi yang meneruskan hasil dari argumen yang diteruskan g
ke f
.
h = \x -> f (g x)
result = h x
Gunakan (.)
saat Anda tidak memiliki argumen yang tersedia untuk diteruskan ke fungsi yang ingin Anda buat.
($)
,, dan kapan menggunakannya($)
adalah fungsi berlaku asosiatif-kanan dengan diutamakan mengikat rendah. Jadi itu hanya menghitung hal-hal di sebelah kanannya terlebih dahulu. Jadi,
result = f $ g x
sama dengan ini, secara prosedural (yang penting karena Haskell dievaluasi dengan malas, itu akan mulai dievaluasi f
terlebih dahulu):
h = f
g_x = g x
result = h g_x
atau lebih ringkas:
result = f (g x)
Gunakan ($)
ketika Anda memiliki semua variabel untuk dievaluasi sebelum Anda menerapkan fungsi sebelumnya pada hasilnya.
Kita dapat melihat ini dengan membaca sumber untuk setiap fungsi.
Inilah sumber untuk (.)
:
-- | 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)
Dan inilah sumber untuk ($)
:
-- | 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
Gunakan komposisi ketika Anda tidak perlu segera mengevaluasi fungsi. Mungkin Anda ingin meneruskan fungsi yang dihasilkan dari komposisi ke fungsi lain.
Gunakan aplikasi saat Anda memasok semua argumen untuk evaluasi penuh.
Jadi untuk contoh kita, secara semantik lebih disukai untuk melakukannya
f $ g x
ketika kita memiliki x
(atau lebih tepatnya, g
argumen), dan lakukan:
f . g
ketika kita tidak melakukannya.
... atau Anda dapat menghindari .
dan $
konstruksi dengan menggunakan pipelining :
third xs = xs |> tail |> tail |> head
Itu setelah Anda menambahkan fungsi helper:
(|>) x y = y x
$
operator Haskell sebenarnya bekerja lebih seperti F # <|
daripada itu |>
, biasanya dalam haskell Anda akan menulis fungsi di atas seperti ini: third xs = head $ tail $ tail $ xs
atau mungkin bahkan seperti third = head . tail . tail
, yang dalam sintaks gaya F # akan menjadi seperti ini:let third = List.head << List.tail << List.tail
Cara yang bagus untuk belajar lebih banyak tentang apa saja (fungsi apa saja) adalah dengan mengingat bahwa semuanya adalah fungsi! Mantra umum itu membantu, tetapi dalam kasus-kasus tertentu seperti operator, ada baiknya mengingat trik kecil ini:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
dan
:t ($)
($) :: (a -> b) -> a -> b
Ingatlah untuk menggunakan :t
secara bebas, dan bungkus operator Anda ()
!
Aturan saya sederhana (saya juga pemula):
.
jika Anda ingin melewatkan parameter (panggil fungsi), dan$
jika belum ada parameter (susun fungsi)Itu adalah
show $ head [1, 2]
tapi tidak pernah:
show . head [1, 2]
Saya pikir contoh singkat dari mana Anda akan menggunakan .
dan tidak $
akan membantu memperjelas hal-hal.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Perhatikan bahwa times6
ini adalah fungsi yang dibuat dari komposisi fungsi.
Semua jawaban lain cukup bagus. Tetapi ada detail kegunaan yang penting tentang bagaimana ghc memperlakukan $, bahwa pemeriksa tipe ghc memungkinkan untuk instatiarion dengan peringkat yang lebih tinggi / jenis terkuantifikasi. Jika Anda melihat jenis $ id
misalnya Anda akan menemukan itu akan mengambil fungsi yang argumennya sendiri merupakan fungsi polimorfik. Hal-hal kecil seperti itu tidak diberi fleksibilitas yang sama dengan operator gangguan yang setara. (Ini benar-benar membuat saya bertanya-tanya apakah $! Layak mendapatkan perawatan yang sama atau tidak)