Seperti yang saya kira memahami model substitusi (dengan transparansi referensial (RT)), Anda dapat menguraikan fungsi menjadi bagian-bagian yang paling sederhana. Jika ekspresi adalah RT, maka Anda dapat menghapus komposisi ekspresi dan selalu mendapatkan hasil yang sama.
Ya, intuisinya benar. Berikut adalah beberapa petunjuk untuk mendapatkan yang lebih tepat:
Seperti yang Anda katakan, setiap ekspresi RT harus memiliki single
"hasil". Artinya, diberi factorial(5)
ekspresi dalam program, itu harus selalu menghasilkan "hasil" yang sama. Jadi, jika seseorang factorial(5)
berada dalam program dan menghasilkan 120, itu harus selalu menghasilkan 120 terlepas dari "urutan langkah" mana yang diperluas / dihitung - terlepas dari waktu .
Contoh: factorial
fungsi.
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
Ada beberapa pertimbangan dengan penjelasan ini.
Pertama-tama, perlu diingat model evaluasi yang berbeda (lihat urutan aplikatif vs normal) dapat menghasilkan "hasil" yang berbeda untuk ekspresi RT yang sama.
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
Dalam kode di atas, first
dan second
secara referensi transparan, namun, ekspresi pada akhirnya menghasilkan "hasil" yang berbeda jika dievaluasi dalam urutan normal dan urutan aplikatif (di bawah yang terakhir, ekspresi tidak berhenti).
.... yang mengarah ke penggunaan "hasil" dalam tanda kutip. Karena tidak diperlukan ekspresi untuk berhenti, itu mungkin tidak menghasilkan nilai. Jadi menggunakan "hasil" agak kabur. Bisa dikatakan ekspresi RT selalu menghasilkan hal yang sama di computations
bawah model evaluasi.
Ketiga, mungkin diperlukan untuk melihat dua yang foo(50)
muncul di program di lokasi yang berbeda sebagai ekspresi yang berbeda - masing-masing menghasilkan hasil mereka sendiri yang mungkin berbeda satu sama lain. Misalnya, jika bahasa memungkinkan ruang lingkup dinamis, kedua ekspresi, meskipun identik secara leksikal, berbeda. Dalam perl:
sub foo {
my $x = shift;
return $x + $y; # y is dynamic scope var
}
sub a {
local $y = 10;
return &foo(50); # expanded to 60
}
sub b {
local $y = 20;
return &foo(50); # expanded to 70
}
Lingkup dinamis menyesatkan karena membuatnya mudah bagi seseorang untuk berpikir x
adalah satu-satunya masukan untuk foo
, padahal pada kenyataannya, itu adalah x
dan y
. Salah satu cara untuk melihat perbedaannya adalah dengan mengubah program menjadi program yang setara tanpa ruang lingkup dinamis - yaitu, memberikan parameter secara eksplisit, jadi alih-alih mendefinisikan foo(x)
, kami mendefinisikan foo(x, y)
dan meneruskan y
secara eksplisit dalam penelepon.
Intinya adalah, kita selalu berada di bawah function
pola pikir: diberi input tertentu untuk ekspresi, kita diberi "hasil" yang sesuai. Jika kita memberikan input yang sama, kita harus selalu mengharapkan "hasil" yang sama.
Sekarang, bagaimana dengan kode berikut?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
The foo
prosedur istirahat RT karena ada redefinitions. Yaitu, kita mendefinisikan y
dalam satu titik, dan yang terakhir, mendefinisikan ulang hal yang sama y
. Dalam contoh perl di atas, y
s adalah binding yang berbeda meskipun mereka memiliki nama huruf yang sama "y". S di sini y
sebenarnya sama. Itu sebabnya kami mengatakan penugasan kembali adalah operasi meta : Anda sebenarnya mengubah definisi program Anda.
Secara kasar, orang biasanya menggambarkan perbedaan sebagai berikut: di pengaturan bebas efek samping, Anda memiliki pemetaan input -> output
. Dalam pengaturan "imperatif", Anda memiliki input -> ouput
konteks state
yang dapat berubah sepanjang waktu.
Sekarang, alih-alih hanya mengganti ekspresi untuk nilai yang sesuai, kita juga harus menerapkan transformasi state
pada setiap operasi yang memerlukannya (dan tentu saja, ekspresi dapat berkonsultasi dengan hal yang sama state
untuk melakukan perhitungan).
Jadi, jika dalam program bebas efek samping semua yang perlu kita ketahui untuk menghitung ekspresi adalah input individualnya, dalam program imperatif, kita perlu mengetahui input dan seluruh kondisi, untuk setiap langkah komputasi. Penalaran adalah yang pertama mengalami pukulan besar (sekarang, untuk men-debug prosedur yang bermasalah, Anda memerlukan input dan dump inti). Trik tertentu dianggap tidak praktis, seperti memoisasi. Tetapi juga, konkurensi dan paralelisme menjadi jauh lebih menantang.
RT
melumpuhkan Anda menonaktifkan penggunaansubstitution model.
Masalah besar dengan tidak dapat menggunakansubstitution model
adalah kekuatan menggunakannya untuk alasan tentang suatu program?