Apakah menggunakan fungsi bersarang memanggil hal yang buruk?

9

Dalam tugas pekerjaan rumah baru-baru ini saya akhirnya memanggil fungsi saya dengan cara yang buruk uglyReceipt(cashParser(cashInput()))program itu sendiri bekerja dengan sempurna tetapi saya masih merasa seperti saya melakukan sesuatu yang salah.

Apakah memanggil fungsi seperti praktik buruk ini dan jika demikian: Apa yang harus saya lakukan?

Carl groth
sumber
Kemungkinan duplikat fungsi bersarang; boleh atau tidak?
nyamuk
1
Apakah fungsi tersebut bersarang, atau apakah itu sarang panggilan ke fungsi? Jika OP yang terakhir mungkin telah (kembali) menemukan pemrograman fungsional.
High Performance Mark
Apa yang membuat Anda berpikir ini akan menjadi praktik yang buruk?
Ixrec
1
@gnat: Pertanyaan itu sama sekali tidak terkait. Pertanyaan itu adalah tentang definisi fungsi bersarang secara leksikal, pertanyaan ini adalah tentang meneruskan hasil pemanggilan fungsi sebagai argumen ke pemanggilan fungsi lain.
Jörg W Mittag
Saya salah membaca - terima kasih telah menunjuk @ JörgWMittag (suara yang ditarik kembali)
nyamuk

Jawaban:

11

Ini sangat tergantung pada seberapa banyak sarang yang Anda gunakan. Bagaimanapun, Anda diizinkan untuk menggunakan hasil fungsi secara langsung dalam ekspresi untuk meningkatkan keterbacaan. Keduanya, kode yang tidak menggunakan ekspresi tersarang (seperti kode assembler), dan kode yang menggunakan terlalu banyak ekspresi tersarang sulit dibaca. Kode yang baik mencoba untuk mencapai keseimbangan di antara yang ekstrem.

Jadi mari kita lihat beberapa contoh. Yang Anda berikan dalam pertanyaan Anda tampaknya cukup sah bagi saya, jadi tidak perlu khawatir di sini. Namun, garis seperti

foo(bar(baz(moo, fab), bim(bam(ext, rel, woot, baz(moo, fab)), noob), bom, zak(bif)));

pasti tidak akan ditoleransi. Demikian juga, kode suka

double xsquare = x*x;
double ysquare = y*y;
double zsquare = z*z;
double xysquare = xsquare + ysquare;
double xyzsquare = xysquare + zsquare;
double length = sqrt(xyzsquare);

tidak akan terlalu mudah dibaca. sqrt(x*x + y*y + z*z)jauh lebih mudah dipahami, meskipun menggabungkan total enam operasi berbeda dalam satu ekspresi.

Saran saya adalah perhatikan ekspresi apa yang masih bisa Anda uraikan dengan mudah. Saat Anda perlu melihat kedua untuk memahami apa yang dilakukan ekspresi tunggal, sekarang saatnya untuk memperkenalkan variabel tambahan.

cmaster - mengembalikan monica
sumber
4

Saya pikir apakah itu baik atau buruk tergantung banyak pada konteksnya. Alasan utama itu mungkin dianggap buruk adalah bahwa hal itu (bisa dibilang) membuat kode lebih sulit untuk dibaca dan di-debug. Ini terutama benar ketika Anda pertama kali belajar memprogram. Saat keterampilan pengkodean (dan kode) Anda semakin maju, ada saatnya hal ini dapat diterima.

Misalnya, pertimbangkan pesan kesalahan seperti ini:

line 1492: missing argument "foo"

Bagaimana Anda tahu jika argumen yang hilang adalah untuk cashInput, cashParseratau uglyReceipt? Tergantung pada bahasa pesan kesalahan mungkin memberitahu Anda, tetapi mungkin tidak.

Jika Anda ingin memecah panggilan fungsi tersebut, dan pesan kesalahan masih mengarahkan Anda ke saluran 1492, Anda akan langsung tahu di mana letak masalahnya:

1491: input = cashInput()
1492: parsed_value = cashParser(input)
1493: receipt = uglyReceipt(parsed_value)

Dengan langkah-langkah yang dipisah secara terpisah, jauh lebih mudah untuk debug karena dimungkinkan untuk menetapkan breakpoint pada langkah apa pun, dan Anda dapat dengan mudah menyuntikkan nilai dengan mengubah nilai variabel lokal.

Bryan Oakley
sumber
3

Konsep yang mendasari pertanyaan Anda sangat penting sehingga saya merasa perlu jawaban lain daripada hanya komentar (seperti yang sudah mulai saya lakukan).

3 jawaban lainnya sejauh ini memberikan beberapa poin pertimbangan yang berguna tentang apakah situasi tertentu pantas menggunakan apa yang Anda sebut "panggilan fungsi bersarang". Tapi mungkin poin yang lebih penting disembunyikan dalam komentar di bawah pertanyaan Anda: jika Anda melewatkan kehalusan dalam apa yang orang-orang terpelajar menyarankan, Carl, Anda telah menemukan sendiri topik yang sebenarnya disebut pemrograman fungsional . Jika Anda belum pernah melihat istilah tersebut, Anda mungkin tidak berpikir itu benar-benar "hal" dalam komentar @ HighPerformanceMark.

Tapi memang benar! Pemrograman fungsional telah ditulis selama puluhan tahun, sejak makalah seminal John Hughes, Why Functional Programming Matters . Ada beberapa bahasa yang merupakan bahasa fungsional (yaitu mereka hanya membiarkan Anda menulis dalam gaya pemrograman fungsional), bahasa seperti Erlang, Lisp, OCaml, atau Haskell. Tetapi ada banyak lagi bahasa yang merupakan hibrida imperatif / bahasa fungsional. Artinya, mereka secara tradisional adalah bahasa imperatif tetapi menawarkan beberapa dukungan untuk pemrograman fungsional juga, termasuk Perl, C ++, Java, C #, dan banyak lagi. Entri Wikipedia tentang pemrograman fungsional menyediakan bagian yang bagus yang menunjukkan perbandingan gaya fungsional vs gaya imperatif untuk sejumlah bahasa.

Ada banyak yang bisa dikatakan tentang perbedaan antara gaya imperatif dan gaya, tetapi titik awal kuncinya adalah bahwa dengan pemrograman fungsional, fungsi atau metode tidak memiliki efek samping , sehingga secara umum lebih mudah untuk memahami dan men-debug program.

Untuk bacaan lebih lanjut, Anda mungkin juga melihat Reginald Braithwaite's Why "Why Functional Programming Matters" Matters dan pos menarik lainnya di SO, Mengapa bahasa fungsional?

Michael Sorens
sumber
2

Ini sama sekali bukan praktik yang buruk secara umum. Fungsi panggilan menerima nilai dan salah satu cara menghasilkan nilai adalah dengan memanggil fungsi lain.

Ketika saya melihat variabel didefinisikan, seperti:

parsed_value = cashParser(input)

... Saya harus mempertimbangkan bahwa parsed_valuemungkin digunakan lebih dari sekali dan saya mungkin harus memeriksa apakah ini benar atau tidak (bagaimana jika perubahan saya merusak sesuatu di tempat lain?). Saat Anda mulai menambahkan lebih banyak variabel, pembaca bisa menjadi lebih kompleks untuk melacak semuanya dan bagaimana mereka terkait. Jadi saya lega ketika saya melihat:

receipt = uglyReceipt(cashParser(input))

... karena ruang lingkup / nilai tengah nilai sudah jelas. Sekarang, seperti biasa, memecah ekspresi panjang menjadi pernyataan terpisah mungkin membantu, terutama jika nama variabel dapat memberikan lebih presisi tentang tujuan suatu nilai:

user_name = input()
coredump
sumber