p
\Ai
\&
>(&]&|0
<*&d
&~bN
10
( )/+
/*
Cobalah online!
Penjelasan
Sejauh ini, ini adalah program yang paling rumit (dan juga yang terpanjang) yang saya tulis di Jellyfish sejauh ini. Saya tidak tahu apakah saya akan dapat memecah ini dengan cara yang dimengerti, tapi saya rasa saya harus mencoba.
Ubur-ubur menyediakan operator iterasi yang cukup umum \
,, yang banyak membantu dengan "menemukan sesuatu yang ke- N ". Salah satu semantiknya adalah "beralih fungsi pada nilai sampai fungsi tes terpisah memberikan sesuatu yang benar" (pada kenyataannya, fungsi tes menerima elemen saat ini dan yang terakhir, tetapi kami hanya akan membuatnya melihat elemen saat ini) . Kita dapat menggunakan ini untuk mengimplementasikan fungsi "angka valid berikutnya". Kelebihan lainnya \
adalah "beralih fungsi pada nilai awal N kali". Kita dapat menggunakan fungsi sebelumnya dan mengulanginya pada 0
N kali, di mana N adalah input. Semua itu diatur cukup ringkas dengan bagian kode ini:
p
\Ai
\&
> 0
(Alasan mengapa 0
, input aktual ke fungsi yang dihasilkan, ada di sana agak rumit dan saya tidak akan membahasnya di sini.)
Masalah dengan semua ini adalah, bahwa kita tidak akan meneruskan nilai saat ini ke fungsi tes secara manual. The \
Operator akan melakukan ini untuk kita. Jadi kita sekarang telah membangun fungsi unary tunggal (melalui komposisi, kait, garpu dan currying) yang mengambil angka dan memberi tahu kita apakah itu nomor yang valid (yaitu yang dibagi dengan jumlah digit dan produk digit). Ini cukup non-sepele ketika Anda tidak bisa merujuk ke argumen. Pernah. Ini keindahan ini:
(&]&|
<*&d
&~bN
10
( )/+
/*
Ini (
adalah pengait unary , yang artinya memanggil fungsi di bawah ( f
) pada inputnya (nilai saat ini x
), dan kemudian meneruskan keduanya ke fungsi pengujian di sebelah kanan (g
), yang dihitungnya g(f(x), x)
.
Dalam kasus kami, f(x)
adalah fungsi komposit lain yang memperoleh pasangan dengan produk digit dan jumlah digit x
. Itu berarti g
akan menjadi fungsi yang memiliki ketiga nilai untuk memeriksa apakahx
valid.
Kami akan mulai dengan melihat bagaimana f
menghitung jumlah digit dan produk digit. Ini adalah f
:
&~b
10
( )/*
/+
&
juga komposisi (tetapi sebaliknya). ~
adalah currying sehingga 10~b
memberikan fungsi yang menghitung angka desimal angka, dan karena kita meneruskannya ke&
dari kanan, itulah hal pertama yang akan terjadi pada input x
. Sisanya menggunakan daftar angka ini untuk menghitung jumlah dan produk mereka.
Untuk menghitung jumlah, kita dapat melipat tambahan di atasnya, yaitu /+
. Demikian juga, untuk menghitung produk kami melipat gandakan dengan itu /*
. Untuk menggabungkan kedua hasil ini menjadi sepasang, kami menggunakan sepasang kait, (
dan )
. Struktur ini adalah:
()g
f
(Dimana f
dan mana g
adalah produk dan jumlah, masing-masing.) Mari kita coba mencari tahu mengapa ini memberi kita sepasang f(x)
dan g(x)
. Perhatikan bahwa hook kanan )
hanya memiliki satu argumen. Dalam hal ini, argumen lain disiratkan sebagai ;
argumen yang membungkus pasangannya. Selanjutnya, kait juga dapat digunakan sebagai fungsi biner (yang akan menjadi kasus di sini) dalam hal ini mereka hanya menerapkan fungsi dalam hanya untuk satu argumen. Jadi sebenarnya )
pada satu fungsi g
memberikan fungsi yang menghitung [x, g(y)]
. Menggunakan ini di hook kiri, bersama dengan f
, kita dapatkan [f(x), g(y)]
. Ini, pada gilirannya digunakan dalam konteks unary, yang berarti bahwa itu sebenarnya dipanggil dengan x == y
dan akhirnya kita sesuai dengan yang [f(x), g(x)]
diperlukan. Fiuh.
Hanya menyisakan satu hal, yang merupakan fungsi pengujian kami sebelumnya g
. Ingatlah bahwa itu akan disebut sebagai di g([p, s], x)
mana x
masih nilai input saat ini, p
adalah produk digitnya dan s
jumlah digitnya. Ini adalah g
:
&]&|
<*&d
N
Untuk menguji kemampuan membagi, kami jelas akan menggunakan modulo, yang ada |
di Jellyfish. Agak luar biasa, ia mengambil operan kanan modulo operannya kiri, yang berarti bahwa argumen g
sudah dalam urutan yang benar (fungsi aritmatika seperti ini secara otomatis memasukkan daftar, jadi ini akan menghitung dua moduli terpisah secara gratis) . Nomor kami dapat dibagi oleh produk dan jumlah, jika hasilnya adalah nol. Untuk memeriksa apakah itu masalahnya, kami memperlakukan pasangan sebagai daftar digit basis-2 ( d
). Hasil dari ini adalah nol, hanya ketika kedua elemen dari pasangan adalah nol, sehingga kita dapat meniadakan hasil dari ini ( N
) untuk mendapatkan nilai kebenaran apakah kedua nilai membagi input. Catat itu |
, d
danN
semua disusun dengan sepasang &
s.
Sayangnya, itu bukan cerita lengkapnya. Bagaimana jika produk digitnya nol? Pembagian dan modulo dengan nol keduanya menghasilkan nol di Jellyfish. Walaupun ini mungkin tampak seperti konvensi yang agak aneh, sebenarnya ternyata agak berguna (karena kita tidak perlu memeriksa nol sebelum melakukan modulo). Namun itu juga berarti kita bisa mendapatkan false positive, jika jumlah digit memang membagi input, tetapi produk digit adalah nol (mis. Input 10
).
Kami dapat memperbaikinya dengan mengalikan hasil pembagian kami dengan produk digit (jadi jika produk digit adalah nol, itu akan mengubah nilai kebenaran kami menjadi nol juga). Ternyata lebih mudah untuk melipatgandakan hasil pembagian dengan pasangan produk dan jumlah, dan mengekstrak hasil dari produk sesudahnya.
Untuk mengalikan hasil dengan pasangan, kita perlu mengembalikan nilai sebelumnya (pasangan). Ini dilakukan dengan garpu ( ]
). Garpu agak seperti kait pada steroid. Jika Anda memberi mereka dua fungsi f
dan g
, mereka mewakili fungsi biner yang menghitung f(a, g(a, b))
. Dalam kasus kami, a
adalah pasangan produk / jumlah, b
adalah nilai input saat ini, g
adalah uji keterbagian kami, dan f
merupakan perkalian. Jadi semua ini menghitung [p, s] * ([p, s] % x == [0, 0])
.
Yang tersisa sekarang adalah mengekstraksi nilai pertama ini, yang merupakan nilai akhir dari fungsi tes yang digunakan dalam iterator. Ini sesederhana membuat ( &
) fork dengan fungsi kepala<
, yang mengembalikan nilai pertama dari daftar.