Operator Titik di Haskell: butuh penjelasan lebih lanjut

87

Saya mencoba memahami apa yang dilakukan operator titik dalam kode Haskell ini:

sumEuler = sum . (map euler) . mkList

Seluruh kode sumber ada di bawah.

Pemahaman saya

Operator titik mengambil dua fungsi sumdan hasil dari map eulerdan hasil mkListsebagai masukan.

Tapi, sumbukankah sebuah fungsi itu adalah argumen dari fungsinya, bukan? Jadi apa yang terjadi disini?

Juga, apa yang (map euler)dilakukannya?

Kode

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
cbrulak.dll
sumber

Jawaban:

139

Sederhananya, .adalah komposisi fungsi, seperti dalam matematika:

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

Dalam kasus Anda, Anda membuat fungsi baru, sumEuleryang juga dapat didefinisikan seperti ini:

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

Gaya dalam contoh Anda disebut gaya "bebas titik" - argumen ke fungsi tersebut dihilangkan. Ini membuat kode lebih jelas dalam banyak kasus. (Mungkin sulit untuk memukul saat pertama kali melihatnya, tetapi Anda akan terbiasa setelah beberapa saat. Ini adalah idiom umum Haskell.)

Jika Anda masih bingung, mungkin membantu untuk menghubungkan .dengan sesuatu seperti pipa UNIX. Jika fkeluaran menjadi gmasukan, yang keluarannya menjadi hmasukan, Anda akan menuliskannya pada baris perintah seperti f < x | g | h. Di Haskell, .bekerja seperti UNIX |, tetapi "mundur" - h . g . f $ x. Saya menemukan notasi ini cukup membantu ketika, katakanlah, memproses daftar. Alih-alih beberapa konstruksi yang berat seperti map (\x -> x * 2 + 10) [1..10], Anda bisa menulis (+10) . (*2) <$> [1..10]. (Dan, jika Anda hanya ingin menerapkan fungsi itu ke satu nilai; itu(+10) . (*2) $ 10 . Konsisten!)

Wiki Haskell memiliki artikel bagus dengan beberapa detail lebih lanjut: http://www.haskell.org/haskellwiki/Pointfree

jrockway
sumber
1
Tiny quibble: cuplikan kode pertama sebenarnya bukan Haskell yang valid.
SwiftsNamesake
2
@SwiftsNamesake Bagi kita yang tidak fasih dalam Haskell, apakah maksud Anda bahwa tanda sama dengan tunggal tidak ada artinya di sini? (Jadi potongannya seharusnya diformat " f (g x)= (f . g) x"?) Atau sesuatu yang lain?
pengguna234461
1
@ user234461 Tepat sekali, ya. Anda akan membutuhkannya ==jika menginginkan Haskell standar yang valid.
SwiftsNamesake
Potongan kecil di bagian atas itu hanyalah emas. Seperti jawaban lain di sini benar tetapi cuplikan itu langsung diklik secara intuitif di kepala saya yang membuatnya tidak perlu membaca sisa jawaban Anda.
Tarick Welling
24

Itu. operator menyusun fungsi. Sebagai contoh,

a . b

Dimana a dan b adalah fungsi adalah fungsi baru yang berjalan b pada argumen, maka hasil tersebut. Kode Anda

sumEuler = sum . (map euler) . mkList

persis sama dengan:

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

tapi semoga lebih mudah dibaca. Alasan adanya parens di sekitar map euler adalah karena memperjelas bahwa ada 3 fungsi yang disusun: sum , map euler dan mkList - map euler adalah fungsi tunggal.

Jesse Rusak
sumber
24

sumadalah fungsi dalam Haskell Prelude, bukan argumen untuk sumEuler. Ini memiliki tipe

Num a => [a] -> a

Operator komposisi fungsi . memiliki tipe

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

Jadi kita punya

           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

Perhatikan bahwa Intini memang sebuah instance dari kelas Numtipe.

Chris Conway
sumber
11

Itu. operator digunakan untuk komposisi fungsi. Sama seperti matematika, jika Anda memiliki fungsi f (x) dan g (x) f. g menjadi f (g (x)).

peta adalah fungsi built-in yang menerapkan fungsi ke daftar. Dengan meletakkan fungsi di dalam tanda kurung, fungsi tersebut diperlakukan sebagai argumen. Istilah untuk ini adalah kari . Anda harus mencarinya.

Apa yang dilakukan adalah bahwa ia mengambil fungsi dengan mengatakan dua argumen, itu menerapkan argumen euler. (map euler) kan? dan hasilnya adalah fungsi baru, yang hanya membutuhkan satu argumen.

jumlah . (peta euler). mkList pada dasarnya adalah cara yang bagus untuk menggabungkan semua itu. Saya harus mengatakan, Haskell saya agak berkarat tapi mungkin Anda bisa menggabungkan fungsi terakhir itu sendiri?

John Leidegren
sumber
6

Operator Titik di Haskell

Saya mencoba memahami apa yang dilakukan operator titik dalam kode Haskell ini:

sumEuler = sum . (map euler) . mkList

Jawaban singkat

Kode yang setara tanpa titik, itu adil

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

atau tanpa lambda

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

karena titik (.) menunjukkan komposisi fungsi.

Jawaban yang lebih panjang

Pertama, mari kita sederhanakan penerapan parsial euleruntuk map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Sekarang kita hanya punya titik-titiknya. Apa yang ditunjukkan oleh titik-titik ini?

Dari sumber :

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

Dengan demikian (.)adalah operator yang compose .

Menyusun

Dalam matematika, kita bisa menulis komposisi fungsi, f (x) dan g (x), yaitu, f (g (x)), sebagai

(f ∘ g) (x)

yang bisa dibaca "f tersusun dengan g".

Jadi di Haskell, f ∘ g, atau f yang terdiri dari g, bisa ditulis:

f . g

Komposisi bersifat asosiatif, yang berarti bahwa f (g (h (x))), yang ditulis dengan operator komposisi, dapat menghilangkan tanda kurung tanpa ambiguitas.

Artinya, karena (f ∘ g) ∘ h ekivalen dengan f ∘ (g ∘ h), kita cukup menulis f ∘ g ∘ h.

Berputar kembali

Kembali ke penyederhanaan kami sebelumnya, ini:

sumEuler = sum . map_euler . mkList

hanya berarti itu sumEuleradalah komposisi yang belum diterapkan dari fungsi-fungsi tersebut:

sumEuler = \x -> sum (map_euler (mkList x))
Aaron Hall
sumber
4

Operator titik menerapkan fungsi di kiri ( sum) ke output fungsi di kanan. Dalam kasus Anda, Anda merangkai beberapa fungsi bersama - Anda meneruskan hasil mkListke (map euler), lalu meneruskan hasilnya ke sum. Situs ini memiliki pengantar yang bagus untuk beberapa konsep.

Andy Mikula
sumber