Tulis fungsi (atau makro) yang mengembalikan true jika dan hanya jika setidaknya salah satu argumennya berisi panggilan ke fungsi itu sendiri dan false jika tidak .
Sebagai contoh:
int a=function(); //a==false
int b=function(0); //b==false
int c=function(function(0)); //c==true
int d=function(3*function(1)+2); //d==true (weakly-typed language)
bool e=function(true||function(1)); //e==true (strongly-typed language)
EDIT: Fungsi / makro dapat memanggil fungsi / makro tambahan lainnya.
EDIT 2: Fungsi harus mengambil setidaknya satu argumen, kecuali bahasa yang digunakan berperilaku seperti C, di mana fungsi yang tidak menggunakan argumen masih dapat dipanggil dengan argumen.
print(func(), func(func()))
, atau apakah hanya akan ada panggilan tingkat atas ke fungsi segera setelah ditetapkan?Jawaban:
Matematika ...
... dibuat untuk ini.
Semuanya adalah ekspresi, dan ekspresi memiliki Kepala dan sejumlah elemen. Jadi
1+2
sebenarnyaPlus[1,2]
, dan{1,2}
sebenarnyaList[1,2]
. Ini berarti kami dapat mencocokkan ekspresi apa pun untuk kepala yang kami minati - dalam hal ini fungsinyaf
sendiri.Yang perlu kita lakukan adalah menemukan cara untuk mencegah Mathematica dari mengevaluasi argumen fungsi sebelum fungsi dipanggil, sehingga kita dapat menganalisis pohon ekspresi dalam fungsi. Untuk apa
Hold
danHoldAll
untuk apa. Mathematica sendiri menggunakan ini ke semua tempat untuk menyediakan kasus khusus untuk implementasi tertentu. Misalnya jika Anda menyebutnyaLength[Permutations[list]]
tidak akan benar-benar membuat semua permutasi dan membuang banyak memori, tetapi sebaliknya menyadari bahwa itu hanya dapat menghitung ini sebagaiLength[list]!
.Mari kita lihat kode di atas secara terperinci, menggunakan panggilan terakhir
f[True || f[1]]
sebagai contoh. Biasanya, Mathematica akan mengevaluasi argumen fungsi terlebih dahulu, jadi ini hanya akan menyebabkan hubungan pendek dan panggilanf[True]
. Namun kami telah menetapkanIni menginstruksikan Mathematica untuk tidak mengevaluasi argumen, sehingga
FullForm
panggilan (yaitu pohon ekspresi internal, tanpa gula sintaksis) adalahDan argumen itu sebenarnya akan diterima
f
dalam bentuk ini. Masalah berikutnya adalah, di dalamf
, segera setelah kami menggunakan argumen, Mathematica akan kembali mencoba untuk mengevaluasinya. Kami dapat menekan ini secara lokal denganHold@x
(sintaksis gula untukHold[x]
). Pada titik ini kita sudah memiliki pegangan pada pohon ekspresi asli dan melakukan apa pun yang kita inginkan.Untuk mencari pola di pohon ekspresi dapat kita gunakan
FreeQ
. Ia memeriksa bahwa pola yang diberikan tidak ditemukan di pohon ekspresi. Kami menggunakan derai_f
, yang cocok dengan subekspresi apa pun dengan kepalaf
(persis apa yang kami cari). Tentu saja,FreeQ
mengembalikan kebalikan dari apa yang kita inginkan, jadi kita meniadakan hasilnya.Satu lagi kehalusan: Saya telah menetapkan argumen sebagai
x___
, yang merupakan urutan 0 atau lebih elemen. Ini memastikan bahwaf
bekerja dengan sejumlah argumen. Dalam panggilan pertama,f[]
ini artinyaHold@x
akan menjadi begitu sajaHold[]
. Jika ada beberapa argumen, sepertif[0,f[1]]
, makaHold@x
akan menjadiHold[0,f[1]]
.Hanya itu yang ada di sana.
sumber
C ++ 11
Mirip dengan templat ekspresi kita dapat menyebarkan fakta bahwa kita memang memanggil fungsi di dalam daftar parameter fungsi dalam tipe pengembaliannya.
Pada awalnya kita membutuhkan beberapa kelas dan fungsi pembantu:
Sekarang, kita dapat menggunakannya untuk kode yang sama persis seperti pada pertanyaan:
Output dari ideone:
sumber
C #
Penggunaan (dalam LINQPad ):
Kuncinya di sini adalah untuk membuat
f
lebih sadar akan parameter, dengan melewati tipe custom (PcgBool
) yang agak mirip boolean.PS Saya harap mengembalikan jenis kustom yang secara implisit dapat dicor dari dan ke bool tidak dianggap sebagai curang. Secara teknis Anda dapat menggunakan nilai kembali seolah-olah itu dari jenis bool, dan pertanyaan yang diajukan untuk "mengembalikan true jika dan hanya jika" dll, tetapi tidak pernah menyatakan jenis kembali harus bool.
sumber
f(new PcgBool())
?Lua
sumber
C ++ 11 TMP
Yang ini sedikit lebih lama. Karena beberapa keterbatasan dalam template C ++ saya harus melakukan semuanya dengan tipe. Jadi "Benar" serta angka-angka diubah menjadi bool dan int. Juga Operasi +, - dan || diubah menjadi tambah, mul dan or_.
Saya harap ini masih memenuhi syarat sebagai jawaban yang valid.
sumber
is_given_foo
lebih disukai daripada yang pertama?C
Saya tidak berpikir itu bisa dilakukan dengan prosedur, tetapi kita selalu dapat (ab) menggunakan makro.
Output ini:
Makro kami
f
melacak kedalaman saat ini, dan kedalaman maksimum yang dicapai sejak dipanggil. Jika yang terakhir lebih besar dari yang pertama, maka telah disebut secara rekursif.sumber
&&
dan||
. Saya dapat mencoba menebus jawaban saya.C, 13
Argumen tidak pernah diperluas, sehingga tidak bisa memanggil makro apa pun. Itu tidak lebih dari seikat teks biasa. Jadi, jawabannya selalu salah.
sumber
F(F(0))
akan dengan senang hati mengevaluasi argumen FF(0)
,. Argumen itu berkembang menjadi 0. Kemudian mengevaluasi F tanpa cat biru pada argumennya 0, menghasilkan 0. Pembatasan non-rekursif tidak berlaku; saat itulah misalnya saya miliki#define F(X) G
dan#define G F(Y)
sedang bermain; dalam hal ini, saat memperluas F (0) ke G, dan kemudian ke F (Y), tokenF
muncul. Karena aku sedang memperluas F, F memiliki cat biru dalam ini kasus, dan dengan demikian ekspansi berhenti di F (Y).X
tidak ada dalam daftar penggantian argumen, makro apa pun yang terikat padanya tidak pernah diperluas. Jika kita menafsirkannya sebagai fungsi yang dipanggil, itu berarti fungsi argumen tidak pernah dipanggil. Ya, saya pikir itu benar.C
Kita bisa menghitung kedalaman rekursi di makro. Kami kemudian menimpa nilai kembali makro luar di makro dalam.
!!b
adalah menormalkan nilai kembali ke boolean. Kode preprocessed berakhir seperti ini:sumber
printf("%d\n", foo(printf("%d\n", foo(1))))
. Panggilan batin untukfoo
mengembalikan 1, tetapi tidak meneleponfoo
.C
Makro membandingkan jika argumennya dimulai dengan "BLAH (".
sumber
BLAH(blub(BLAH(0)))
.Algol 60
Berikut adalah
boolean procedure
yang melakukan apa yang ditanyakan oleh pertanyaan (catatan: Algol 60 didefinisikan dalam hal daftar token tanpa memperbaiki sintaks untuk mereka, di bawah ini menggunakan sintaks Marst untuk mewakili token individu yang membentuk program):Verifikasi
Berikut kode tes yang saya gunakan:
Seperti yang diharapkan, outputnya adalah:
Penjelasan
Algol 60 memiliki urutan evaluasi yang berbeda dari kebanyakan bahasa, yang memiliki logika sendiri, dan sebenarnya jauh lebih kuat dan umum daripada urutan evaluasi yang khas, tetapi cukup sulit bagi manusia untuk mendapatkan kepala mereka sekitar (dan juga cukup sulit untuk komputer untuk diimplementasikan secara efisien, itulah sebabnya ia diubah untuk Algol 68). Ini memungkinkan untuk solusi tanpa kecurangan (program tidak perlu melihat pohon parse atau semacamnya, dan tidak seperti hampir semua solusi lain di sini, ini akan bekerja dengan baik jika panggilan bersarang dilakukan melalui FFI).
Saya juga memutuskan untuk memamerkan beberapa kebiasaan bahasa lainnya. (Khususnya, nama variabel dapat berisi spasi putih; ini cukup berguna untuk dibaca, karena mereka tidak dapat berisi garis bawah. Saya juga menyukai kenyataan bahwa indikator komentar adalah kata literal
comment
dalam sebagian besar penyandian sintaksis. Algol 68 menemukan ini cukup canggung untuk pendeknya komentar, dan diperkenalkan¢
sebagai alternatif. Kutipan di sekitar badan komentar biasanya tidak diperlukan, saya hanya menambahkannya untuk kejelasan dan untuk mencegah komentar secara tidak sengaja berakhir ketika saya mengetikkan titik koma.) Saya sebenarnya sangat menyukai konsep luas bahasa (jika bukan detailnya), tetapi sangat verbose sehingga saya jarang menggunakannya di PPCG.Cara utama di mana Algol 60 berbeda dari bahasa yang diilhami (seperti Algol 68, dan secara tidak langsung C, Java, dll; orang yang tahu K&R C mungkin akan mengenali sintaks ini untuk fungsi) adalah bahwa argumen fungsi diperlakukan agak seperti lambda kecil sendiri; misalnya, jika Anda memberikan argumen
5
ke fungsi yang hanya angka 5, tetapi jika Anda memberikan argumenx+1
Anda mendapatkan apa yang Anda tentukan, konsep "x
plus 1", bukan hasil darix
plus 1. Perbedaannya di sini adalah bahwa jikax
perubahan, kemudian mencoba untuk mengevaluasi argumen fungsi yang bersangkutan akan melihat baru nilaix
. Jika argumen fungsi tidak dievaluasi di dalam fungsi, itu tidak akan dievaluasi di luar fungsi juga; juga, jika dievaluasi beberapa kali di dalam fungsi, itu akan dievaluasi secara terpisah setiap kali (dengan asumsi bahwa ini tidak dapat dioptimalkan). Ini berarti bahwa dimungkinkan untuk melakukan hal-hal seperti menangkap fungsi, katakanlah,if
atauwhile
dalam suatu fungsi.Dalam program ini, kami mengeksploitasi fakta bahwa jika panggilan ke suatu fungsi muncul dalam argumen ke fungsi itu, itu berarti bahwa fungsi tersebut akan berjalan secara rekursif (karena argumen tersebut dievaluasi tepat pada titik atau titik yang dievaluasi oleh fungsi fungsi itu. , tidak lebih awal atau lebih lambat, dan ini harus berada di dalam fungsi body). Ini mengurangi masalah untuk mendeteksi jika fungsi berjalan secara rekursif, yang jauh lebih mudah; yang Anda butuhkan adalah variabel thread-lokal yang merasakan jika ada panggilan rekursif (ditambah, dalam hal ini, yang lain untuk mengkomunikasikan informasi kembali dengan cara lain). Kita dapat menggunakan variabel statis (mis
own
) untuk tujuan tersebut, karena Algol 60 adalah single-threaded. Yang harus kita lakukan setelah itu adalah mengembalikan semuanya seperti semula, sehingga fungsinya akan berfungsi dengan benar jika dipanggil beberapa kali (seperti yang disyaratkan oleh aturan PPCG).Fungsi tidak mengembalikan nilai yang diinginkan dari panggilan dalam saat ini (setidaknya jika Anda menganggap mereka harus mencari panggilan sendiri hanya dalam argumen mereka , daripada menghitung sendiri); membuat pekerjaan itu cukup mudah menggunakan prinsip-prinsip umum yang sama, tetapi lebih kompleks dan akan mengaburkan cara kerja fungsi. Jika itu dianggap perlu untuk mematuhi pertanyaan, itu seharusnya tidak terlalu sulit untuk diubah.
sumber
Jawa
dapat
reset()
dihitung sebagai tambahan?Keluaran:
EDIT
Ini adalah versi lain yang tidak menggunakan
reset()
metode ini, tetapi banyak kegilaan. Itu menciptakan dan mengkompilasi pada saat runtime kode di atas dengan panggilan ke fungsi yang dilewati sebagai argumen di stdin. Saya ingin solusi yang lebih elegan tetapi sayangnya saya tidak punya terlalu banyak waktu untuk ini :(Untuk menjalankannya cukup panggil misalnya
javac FuncOFunc.java function(function(1),function(),4)
.sumber
reset()
setelah setiapfunction
doa. Gagasan fungsi tambahan adalah untuk memungkinkan Anda menggunakan metode pribadi lainnya dari dalamfunction
tubuh ... Tapi itu hanya interpretasi saya dari pertanyaan, mari serahkan kepada penanya untuk memutuskan.reset()
versi yang kurang. Masih kode di atas berfungsi jika hanya ada satu panggilan di main (tanpa jumlah variabel dan fungsi reset)Python
Simpan sebagai
selfarg.py
dan jalankan ataufrom selfarg import function
dalam skrip lain. Itu tidak bekerja di repl.Menggunakan frame stack saat ini dan luar
function
mendapatkan nama dan tempat panggilan (file dan nomor saluran). Ini kemudian membuka file yang diperoleh dan mem-parsingnya menjadi pohon sintaksis abstrak. Itu melompati panggilan fungsi yang diidentifikasi oleh nomor baris dan memeriksa apakah ada panggilan fungsi lain dengan nama yang sama dalam argumennya.sunting : Tidak apa-apa dengan python2 juga. Mengubah python3 menjadi python dalam judul.
sumber