2012-08-24 34 views
8

Tôi hiểu các khái niệm khác nhau về lập trình chức năng của chính nó: các hiệu ứng phụ, tính bất biến, hàm thuần túy, tính minh bạch tham chiếu. Nhưng tôi không thể kết nối chúng lại với nhau trong đầu. Ví dụ: Tôi có các câu hỏi sau:Quan hệ giữa các khái niệm lập trình chức năng khác nhau

  1. Mối quan hệ giữa ref là gì. minh bạch và bất biến. Người ta có ngụ ý người kia không?

  2. Đôi khi các tác dụng phụ và bất biến được sử dụng thay thế cho nhau. Nó có đúng không?

+0

bạn quên "idempotence" cũng: P – mergeconflict

Trả lời

2

"Không" để là người đầu tiên - một ngụ ý khác, nhưng không phải là ngược lại, và đủ điều kiện "Có" đến từng giây.

  1. "An expression is said to be referentially transparent if it can be replaced with its value without changing the behavior of a program". Không thể nhập đầu vào cho thấy rằng một biểu thức (hàm) sẽ luôn luôn đánh giá theo cùng một giá trị, và do đó là tham chiếu trong suốt.

    Tuy nhiên, (mergeconflict đã sửa lỗi cho tôi vào thời điểm này) là tham chiếu trong suốt không nhất thiết yêu cầu bất biến.

  2. Theo định nghĩa, tác dụng phụ là một khía cạnh của hàm ; có nghĩa là khi bạn gọi một hàm, nó sẽ thay đổi một cái gì đó. Tính không thay đổi là một khía cạnh của dữ liệu; nó không thể thay đổi được. Việc gọi một hàm có nghĩa là không có tác dụng phụ. (trong Scala, đó là giới hạn "không có thay đổi đối với các đối tượng bất biến (s)" - nhà phát triển có trách nhiệm và quyết định).

    Trong khi tác dụng phụtính bất biến không có nghĩa là điều tương tự, họ là những khía cạnh liên quan chặt chẽ của một hàm và dữ liệu chức năng được áp dụng cho.

Kể từ Scala không phải là một ngôn ngữ lập trình chức năng thuần túy, người ta phải cẩn thận khi xem xét ý nghĩa của những câu như "đầu vào không thay đổi" - phạm vi của đầu vào cho một chức năng có thể bao gồm các yếu tố khác so với những người đã thông qua làm tham số. Tương tự như vậy để xem xét tác dụng phụ.

+0

Tôi thích làm rõ của bạn trong (2) các tác dụng phụ có liên quan đến chức năng, trong khi bất biến có liên quan đến dữ liệu, nhưng tôi nghĩ trong (1) là sai. Tính minh bạch tham chiếu không liên quan trực tiếp đến tính biến đổi, thay vì nó liên quan đến môi trường, như được chỉ ra trong báo giá bạn đưa vào. – mergeconflict

+0

@mergeconflict cảm ơn bạn đã sửa và chi tiết trong phản hồi của bạn! Câu trả lời được điều chỉnh cho phù hợp. –

1

Nó khá phụ thuộc vào định nghĩa cụ thể mà bạn sử dụng (có thể có bất đồng ý kiến, xem ví dụ Purity vs Referential transparency), nhưng tôi nghĩ rằng đây là một giải thích hợp lý:

minh bạch tham chiếu và 'tinh khiết' được tính chất của chức năng/biểu thức . Một hàm/biểu thức có thể có hoặc không có tác dụng phụ. Tính bất biến, mặt khác, là một thuộc tính của các đối tượng, không phải là các hàm/biểu thức.

Minh bạch tham chiếu, tác dụng phụ và độ tinh khiết có liên quan chặt chẽ: 'thuần' và 'tham chiếu minh bạch' là tương đương và các khái niệm đó tương đương với sự vắng mặt của các tác dụng phụ.

Một đối tượng không thay đổi có thể có các phương thức không minh bạch tham chiếu: các phương pháp này sẽ không thay đổi đối tượng (điều đó sẽ làm cho đối tượng có thể thay đổi), nhưng có thể có các tác dụng phụ khác như thực hiện I/O hoặc thao tác (có thể thay đổi) thông số.

8

Câu hỏi này yêu cầu một số câu trả lời đặc biệt khó tính, vì đó là về cách xác định từ vựng chung.

Đầu tiên, hàm là một loại quan hệ toán học giữa "miền" của đầu vào và "phạm vi" (hoặc tên miền) của đầu ra. Mỗi đầu vào tạo ra một đầu ra rõ ràng. Ví dụ, hàm bổ sung số nguyên + chấp nhận đầu vào trong miền Int x Int và tạo kết quả đầu ra trong phạm vi Int.

object Ex0 { 
    def +(x: Int, y: Int): Int = x + y 
} 

Với bất kỳ giá trị cho xy, rõ ràng + sẽ luôn tạo ra kết quả tương tự. Đây là một hàm. Nếu trình biên dịch thông minh hơn, nó có thể chèn mã để lưu vào bộ nhớ cache kết quả của hàm này cho mỗi cặp đầu vào và thực hiện tra cứu bộ nhớ cache làm tối ưu hóa. Rõ ràng là an toàn ở đây.

Vấn đề là trong phần mềm, thuật ngữ "hàm" đã phần nào bị lạm dụng: mặc dù hàm chấp nhận đối số và trả về giá trị như được khai báo trong chữ ký của chúng, chúng cũng có thể đọc và ghi vào một số ngữ cảnh bên ngoài. Ví dụ:

class Ex1 { 
    def +(x: Int): Int = x + Random.nextInt 
} 

Chúng tôi không thể nghĩ về điều này như một hàm toán học nữa, bởi vì đối với một giá trị nhất định của x, + có thể tạo ra kết quả khác nhau (tùy thuộc vào một giá trị ngẫu nhiên, mà không xuất hiện bất cứ nơi nào trong + 's chữ ký). Kết quả của + không thể được lưu trữ an toàn như được mô tả ở trên. Vì vậy, bây giờ chúng tôi có một vấn đề từ vựng, mà chúng tôi giải quyết bằng cách nói rằng Ex0.+tinh khiếtEx1.+ thì không.

Được rồi, vì hiện tại chúng tôi đã chấp nhận một mức độ tạp chất nào đó, chúng tôi cần xác định loại tạp chất nào mà chúng tôi đang nói đến! Trong trường hợp này, chúng tôi đã nói sự khác biệt là chúng ta có thể cache Ex0.+ 'kết quả của kết hợp với đầu vào của nó xy, và rằng chúng ta không thể bộ nhớ cache Ex1.+' s kết quả liên quan đến đầu vào của nó x. Thuật ngữ chúng tôi sử dụng để mô tả khả năng lưu trữ (hoặc, đúng hơn, khả năng thay thế của một cuộc gọi hàm với đầu ra của nó) là tính minh bạch tham chiếu.

Tất cả các hàm thuần túy đều có tính minh bạch tham chiếu, nhưng một số hàm trong suốt tham chiếu không thuần túy. Ví dụ:

object Ex2 { 
    var lastResult: Int 
    def +(x: Int, y: Int): Int = { 
    lastResult = x + y 
    lastResult 
    } 
} 

Ở đây chúng ta không đọc từ bất kỳ bối cảnh bên ngoài, và giá trị sản xuất bởi Ex2.+ cho bất kỳ đầu vào xysẽ luôn là khả năng lưu nhớ, như trong Ex0. Đây là tham chiếu trong suốt, nhưng nó có hiệu ứng phụ , để lưu trữ giá trị cuối cùng được tính theo hàm. Người khác có thể đến sau và lấy lastResult, điều này sẽ cung cấp cho họ một số thông tin chi tiết lén lút về những gì đang xảy ra với Ex2.+!

Một mặt lưu ý: bạn cũng có thể tranh luận rằng Ex2.+không referentially minh bạch, bởi vì mặc dù bộ nhớ đệm là an toàn đối với kết quả của chức năng với, tác dụng phụ là âm thầm bỏ qua trong trường hợp của một bộ nhớ cache " đánh." Nói cách khác, việc giới thiệu bộ nhớ cache thay đổi ý nghĩa của chương trình, nếu tác dụng phụ là quan trọng (do đó, Norman Ramsey's comment)! Nếu bạn thích định nghĩa này, thì hàm phải thuần khiết để có tính minh bạch trong suốt.

Bây giờ, một điều cần lưu ý ở đây là nếu chúng tôi gọi Ex2.+ hai lần trở lên liên tiếp với cùng một yếu tố đầu vào, lastResult sẽ không thay đổi. Tác dụng phụ của việc gọi phương thức n lần tương đương với tác dụng phụ của việc chỉ gọi phương thức một lần và vì vậy chúng tôi nói rằng Ex2.+idempotent. Chúng tôi có thể thay đổi:

object Ex3 { 
    var history: Seq[Int] 
    def +(x: Int, y: Int): Int = { 
    result = x + y 
    history = history :+ result 
    result 
    } 
} 

Hiện tại, mỗi lần chúng tôi gọi Ex3.+, thay đổi lịch sử, do đó chức năng không còn là không có giá trị.

Được rồi, bản tóm tắt cho đến thời điểm này: hàm thuần túy là chức năng không đọc hoặc ghi vào bất kỳ ngữ cảnh bên ngoài nào. Đó là cả hai có liên quan trong suốttác dụng phụ miễn phí. Một hàm đọc từ một số ngữ cảnh bên ngoài không còn minh bạch tham chiếu nữa, trong khi một hàm viết cho một số ngữ cảnh bên ngoài không còn miễn phí các tác dụng phụ nữa. Và cuối cùng, một hàm mà khi được gọi nhiều lần với cùng một đầu vào có cùng tác dụng phụ khi gọi nó chỉ một lần, được gọi là idempotent. Lưu ý rằng một hàm không có tác dụng phụ, chẳng hạn như hàm thuần túy, cũng là không có giá trị!

Vì vậy, làm thế nào để khả năng biến đổibất biến phát tất cả điều này? Vâng, nhìn lại Ex2Ex3. Họ giới thiệu các biến thể var s. Các tác dụng phụ của Ex2.+Ex3.+ là để biến đổi var s tương ứng của chúng! Vì vậy, khả năng biến đổi và tác dụng phụ đi đôi với nhau; một hàm chỉ hoạt động trên dữ liệu bất biến phải là tác dụng phụ miễn phí. Nó có thể vẫn không thuần khiết (có nghĩa là, nó có thể không được tham chiếu trong suốt), nhưng ít nhất nó sẽ không tạo ra các tác dụng phụ.

Câu hỏi tiếp theo hợp lý cho câu hỏi này có thể là: "lợi ích của phong cách chức năng thuần túy là gì?" Câu trả lời cho câu hỏi đó có liên quan nhiều hơn;)

+0

Mô tả rất hay về 'thuần khiết', cảm ơn vì điều đó. Tôi không đồng ý với ví dụ của bạn 'Ex2' về tính minh bạch tham chiếu, mặc dù: hiệu ứng phụ này có thể nhìn thấy ở phần còn lại của chương trình (vì nó có thể đọc 'lastResult'), có nghĩa là áp dụng ghi nhớ cho phương pháp này sẽ thay đổi kết quả/hành vi quan sát được của chương trình. Điều đó làm cho phương thức này không minh bạch tham chiếu - tôi không biết bất kỳ định nghĩa nào không đồng ý. –

+0

Vùng xám mà bạn có thể nghĩ đến là bộ nhớ riêng tư, không thể truy cập vào phần còn lại của chương trình: phương pháp lưu trữ kết quả trung gian trong bộ nhớ cache như vậy không có tác dụng phụ (lấp đầy bộ đệm), nhưng áp dụng ví dụ: việc ghi nhớ không làm thay đổi kết quả của chương trình. Cho dù một phương pháp được coi là 'minh bạch tham chiếu' thực sự có thể gây tranh cãi hay không. –

+0

Như @ArnoutEngelen đã nói, nó là một chút khó hiểu những gì bạn có nghĩa là với các tác dụng phụ liên quan trong suốt <->. Bạn có thể cung cấp một ví dụ có tính minh bạch tham chiếu nhưng không có tác dụng phụ và không thuần khiết không? – sschaef

Các vấn đề liên quan