Saya telah melihatnya dalam paradigma imperatif
f (x) + f (x)
mungkin tidak sama dengan:
2 * f (x)
Tetapi dalam paradigma fungsional itu harus sama. Saya telah mencoba untuk mengimplementasikan kedua kasus dalam Python dan Skema , tetapi bagi saya mereka terlihat cukup mudah sama.
Apa yang akan menjadi contoh yang bisa menunjukkan perbedaan dengan fungsi yang diberikan?
f(x++)+f(x++)
mungkin tidak sama dengan2*f(x++)
(di C itu terutama indah ketika hal-hal seperti itu tersembunyi di dalam makro - apakah saya mematahkan hidung saya pada itu? Anda bertaruh)f(x++)+f(x++)
bisa benar-benar apa saja, karena itu memanggil perilaku yang tidak terdefinisi. Tapi itu tidak benar-benar terkait dengan transparansi referensial - yang tidak akan membantu untuk panggilan ini, itu 'tidak terdefinisi' untuk fungsi transparan referensial sepertisin(x++)+sin(x++)
juga. Bisa jadi 42, bisa memformat hard drive Anda, bisa membuat setan terbang keluar dari hidung pengguna ...Jawaban:
Transparansi referensial, disebut fungsi, menunjukkan bahwa Anda dapat menentukan hasil penerapan fungsi itu hanya dengan melihat nilai argumennya. Anda dapat menulis fungsi yang secara transparan transparan dalam bahasa pemrograman apa pun, misalnya Python, Skema, Pascal, C.
Di sisi lain, dalam sebagian besar bahasa Anda juga dapat menulis fungsi yang tidak transparan secara referensial. Misalnya, fungsi Python ini:
sebenarnya tidak transparan, pada kenyataannya panggilan
dan
akan menghasilkan nilai yang berbeda, untuk argumen apa pun
x
. Alasan untuk ini adalah bahwa fungsi menggunakan dan memodifikasi variabel global, oleh karena itu hasil setiap doa tergantung pada keadaan yang berubah ini, dan tidak hanya pada argumen fungsi.Haskell, sebuah bahasa yang sepenuhnya fungsional, secara ketat memisahkan evaluasi ekspresi di mana fungsi - fungsi murni diterapkan dan yang selalu transparan secara referensi, dari pelaksanaan tindakan (pemrosesan nilai-nilai khusus), yang tidak transparan secara referensial, yaitu melaksanakan tindakan yang sama dapat dilakukan setiap kali hasil berbeda.
Jadi, untuk fungsi Haskell
dan bilangan bulat apa pun
x
, selalu benar ituContoh tindakan adalah hasil dari fungsi perpustakaan
getLine
:Sebagai hasil dari evaluasi ekspresi, fungsi ini (sebenarnya konstan) pertama-tama menghasilkan nilai tipe murni
IO String
. Nilai dari tipe ini adalah nilai seperti yang lain: Anda bisa menyebarkannya, meletakkannya di struktur data, menyusunnya menggunakan fungsi khusus, dan sebagainya. Misalnya, Anda dapat membuat daftar tindakan seperti:Tindakan khusus karena Anda dapat memberitahu runtime Haskell untuk menjalankannya dengan menulis:
Dalam hal ini, ketika program Haskell Anda dimulai, runtime berjalan melalui tindakan terikat
main
dan mengeksekusinya , mungkin menghasilkan efek samping. Oleh karena itu, eksekusi tindakan tidak transparan secara referensial karena mengeksekusi aksi yang sama dua kali dapat menghasilkan hasil yang berbeda tergantung pada apa yang didapat runtime sebagai input.Berkat sistem tipe Haskell, sebuah aksi tidak pernah dapat digunakan dalam konteks di mana tipe lain diharapkan, dan sebaliknya. Jadi, jika Anda ingin menemukan panjang string, Anda dapat menggunakan
length
fungsi:akan kembali 5. Tetapi jika Anda ingin menemukan panjang string dibaca dari terminal, Anda tidak dapat menulis
karena Anda mendapatkan kesalahan tipe:
length
mengharapkan input dari daftar jenis (dan sebuah String, memang, daftar) tetapigetLine
merupakan nilai tipeIO String
(suatu tindakan). Dengan cara ini, sistem tipe memastikan bahwa nilai aksi sepertigetLine
(yang pelaksanaannya dilakukan di luar bahasa inti dan yang mungkin transparan non-referensial) tidak dapat disembunyikan di dalam nilai tipe non-tindakan jenisInt
.EDIT
Untuk menjawab pertanyaan yang ada, berikut ini adalah program Haskell kecil yang membaca garis dari konsol dan mencetak panjangnya.
Tindakan utama terdiri dari dua subaksi yang dieksekusi berurutan:
getline
dari jenisIO String
,putStrLn
tipeString -> IO ()
pada argumennya.Lebih tepatnya, aksi kedua dibangun oleh
line
nilai yang dibaca oleh tindakan pertama,length
(menghitung panjang sebagai integer) dan kemudianshow
(mengubah integer menjadi sebuah string),putStrLn
pada hasilshow
.Pada titik ini, tindakan kedua dapat dijalankan. Jika Anda mengetik "Halo", itu akan mencetak "5".
Perhatikan bahwa jika Anda mendapatkan nilai dari suatu tindakan menggunakan
<-
notasi, Anda hanya dapat menggunakan nilai itu di dalam tindakan lain, misalnya Anda tidak bisa menulis:karena
show (length line)
bertipeString
sedangkan notasi mensyaratkan bahwa suatu tindakan (getLine
bertipeIO String
) diikuti oleh tindakan lain (misalnyaputStrLn (show (length line))
bertipeIO ()
).EDIT 2
Definisi Jörg W Mittag tentang transparansi referensial lebih umum daripada definisi saya (saya telah meningkatkan jawabannya). Saya telah menggunakan definisi terbatas karena contoh dalam pertanyaan ini berfokus pada nilai balik fungsi dan saya ingin menggambarkan aspek ini. Namun, RT secara umum merujuk pada makna keseluruhan program, termasuk perubahan keadaan global dan interaksi dengan lingkungan (IO) yang disebabkan oleh evaluasi ekspresi. Jadi, untuk definisi umum yang benar, Anda harus merujuk ke jawaban itu.
sumber
IO
tipe Haskell dengan cukup mudah dalam bahasa apa pun dengan lambdas dan generik, tetapi karena siapa pun dapat meneleponprintln
langsung, implementasiIO
tidak menjamin kemurnian; itu hanya sebuah konvensi.getLine
tidak transparan referensial salah. Anda menampilkangetLine
seolah-olah mengevaluasi atau mengurangi beberapa String, String tertentu yang tergantung pada input pengguna. Ini salah.IO String
tidak mengandung String lagiMaybe String
.IO String
adalah resep untuk mungkin, mungkin mendapatkan String dan, sebagai ungkapan, itu murni seperti yang lainnya di Haskell.Namun, bukan itu yang dimaksud Transparansi Referensial. RT berarti bahwa Anda dapat mengganti ekspresi apa pun dalam program dengan hasil mengevaluasi ekspresi itu (atau sebaliknya) tanpa mengubah arti program.
Ambil, misalnya, program berikut:
Program ini transparan secara referensial. Saya bisa mengganti satu atau kedua kejadian
f()
dengan2
dan masih akan bekerja sama:atau
atau
semua akan berperilaku sama.
Sebenarnya, saya curang. Saya harus bisa mengganti panggilan
print
dengan nilai kembalinya (yang tidak ada nilai sama sekali) tanpa mengubah arti program. Namun, jelas, jika saya hanya menghapus duaprint
pernyataan, makna program akan berubah: sebelumnya, ia mencetak sesuatu ke layar, setelah itu tidak. I / O tidak transparan secara referensi.Aturan praktis yang sederhana adalah: jika Anda dapat mengganti setiap ekspresi, sub-ekspresi atau panggilan subrutin dengan nilai balik dari ekspresi, sub-ekspresi atau panggilan subrutin di mana saja dalam program, tanpa program mengubah artinya, maka Anda memiliki referensi transparansi. Dan apa artinya ini, secara praktis adalah bahwa Anda tidak dapat memiliki I / O, tidak dapat memiliki keadaan yang dapat berubah, tidak dapat memiliki efek samping. Dalam setiap ekspresi, nilai ekspresi harus semata-mata bergantung pada nilai-nilai bagian penyusun ekspresi. Dan dalam setiap panggilan subrutin, nilai balik harus sepenuhnya bergantung pada argumen.
sumber
print
contohnya. Mungkin salah satu cara untuk melihat ini, adalah apa yang dicetak di layar adalah bagian dari "nilai pengembalian". Jika Anda dapat menggantiprint
dengan nilai pengembalian fungsinya dan penulisan yang setara di terminal, contohnya berfungsi.4
dan2 + 2
tidak dapat dipertukarkan karena mereka memiliki waktu berjalan yang berbeda, dan seluruh titik transparansi referensial adalah bahwa Anda dapat mengganti ekspresi dengan apa pun yang dievaluasi. Pertimbangan penting adalah keamanan benang.listOfSequence.append(n)
kembaliNone
, sehingga Anda harus dapat mengganti setiap panggilanlistOfSequence.append(n)
denganNone
tanpa mengubah arti program Anda. Bisakah kamu melakukan itu? Jika tidak, maka tidak transparan referensial.Sebagian dari jawaban ini diambil langsung dari tutorial yang belum selesai tentang pemrograman fungsional , yang dihosting di akun GitHub saya:
Pertimbangkan contoh sederhana:
Dalam bahasa fungsional murni, sisi kiri dan kanan tanda sama dengan dapat disubstitusikan untuk satu sama lain. Artinya, tidak seperti dalam bahasa seperti C, notasi di atas benar-benar menegaskan kesetaraan. Konsekuensi dari ini adalah bahwa kita dapat alasan tentang kode program seperti persamaan matematika.
Dari Haskell wiki :
Untuk kontras ini, jenis operasi yang dilakukan oleh bahasa mirip C kadang-kadang disebut sebagai tugas destruktif .
Istilah murni sering digunakan untuk menggambarkan properti ekspresi, yang relevan dengan diskusi ini. Agar suatu fungsi dianggap murni,
Menurut metafora kotak hitam, yang ditemukan di banyak buku teks matematika, fungsi internal sepenuhnya tertutup dari dunia luar. Efek samping adalah ketika suatu fungsi atau ekspresi melanggar prinsip ini - yaitu, prosedur diizinkan untuk berkomunikasi dengan beberapa cara dengan unit program lain (misalnya untuk berbagi dan bertukar informasi).
Singkatnya, transparansi referensial adalah suatu keharusan bagi fungsi untuk berperilaku seperti benar , fungsi matematika juga dalam semantik bahasa pemrograman.
sumber
[here](link to source)
..." diikuti oleh 2) format kutipan yang tepat (gunakan tanda kutip, atau lebih baik lagi,>
simbol untuk itu). Juga tidak ada salahnya jika selain memberikan panduan umum, alamat menjawab pertanyaan konkret yang ditanyakan, dalam hal ini tentangf(x)+f(x)
/2*f(x)
, lihat Bagaimana Menjawab - jika tidak, Anda mungkin hanya mengiklankan halaman Anda