Tại sao println được coi là một hàm không tinh khiết?


10

Tôi đang đọc chương trình sách trong scala và người ta nói:

... trong trường hợp này, tác dụng phụ của nó là in ra luồng đầu ra tiêu chuẩn.

và tôi không thấy tác dụng phụ ở đâu, vì, với cùng một đầu vào, println sẽ in cùng một đầu ra (tôi nghĩ)
CẬP NHẬT
ví dụ bất cứ khi nào chúng tôi gọi:

println(5)

nó sẽ in 5 , tôi không thấy trường hợp nào cuộc gọi println(5)sẽ in một giá trị khác 5 !!


nếu điều này trả lời câu hỏi của bạn, tôi sẽ xóa phần mềm
tôi.stackexchange.com / q / 40297/271736

3
Câu trả lời tại Minh bạch tham chiếu là gì? có vẻ có liên quan ở đây.
Nathan Hughes


2
Bạn nhầm lẫn tác dụng phụ (không minh bạch tham chiếu) với xác định. printlnlà một hàm xác định nhưng để được thuần túy thì nó cũng phải là RT.
bob

2
Bởi vì nó làm một cái gì đó ngoài việc tính toán một kết quả và trả lại nó.
Seth Tisue

Câu trả lời:


6

Bạn có thể biết nếu một biểu thức có tác dụng phụ bằng cách thay thế biểu thức bằng kết quả của nó. Nếu chương trình thay đổi ý nghĩa , sẽ có tác dụng phụ. Ví dụ,

println(5)

là một chương trình khác nhau để

()

Đó là, một tác dụng phụ là bất kỳ hiệu ứng có thể quan sát được không được mã hóa trong kết quả đánh giá một biểu thức. Kết quả là đây (), nhưng không có gì trong giá trị đó mã hóa thực tế là 5 hiện đã xuất hiện ở đâu đó trên màn hình của bạn.


6
Trên thực tế đó không phải là một định nghĩa tốt về "tác dụng phụ" - Một tác dụng phụ có thể được định nghĩa là bất cứ điều gì phá vỡ tính minh bạch tham chiếu. Những gì bạn đã cố gắng thể hiện ở đây là RT nhưng ví dụ của bạn là sai. Vì thực hiện một cái gì đó nhiều lần nên làm cùng một thứ nhiều lần - Thay vào đó, val a = println("hello"); val b = (a, a)nên giống như val b = (pritnln("hello"), println("hello")).
Luis Miguel Mejía Suárez

1
@ LuisMiguelMejíaSuárez có lẽ ví dụ của tôi không rõ ràng, nhưng tôi không nghĩ nó sai. Về cơ bản, tôi chỉ ra sự khác biệt giữa chương trình println(5)(). Hay bạn có nghĩa là câu cuối cùng?
joelb

Vâng, nhưng bạn không rõ về nó. Vì, như tôi đã nói, vấn đề không phải là gọi một cái gì đó nhiều lần, vấn đề là nếu thay thế một tham chiếu bằng định nghĩa của nó sẽ có tác động đó.
Luis Miguel Mejía Suárez

Tôi không hiểu rõ ví dụ của bạn
aName

5
Tôi đã nói sai vì hoàn toàn có thể có tác dụng phụ và không có tác dụng nên việc lặp lại nó không làm thay đổi hiệu ứng. Ví dụ: gán cho một biến có thể thay đổi; Làm thế nào bạn có thể phân biệt x = 1x = 1; x = 1; x = 1?
Alexey Romanov

5

Hãy xem xét sự tương tự sau đây

var out: String = ""
def myprintln(s: String) = {
  out += s // this non-local mutation makes me impure
  ()
}

Ở đây myprintlnkhông tinh khiết bởi vì bên cạnh giá trị trả về, ()nó cũng biến đổi biến không cục bộ outthành hiệu ứng phụ. Bây giờ hãy tưởng tượng outlà dòng vanilla printlnđột biến.


1
cảm ơn bạn, vì đã trả lời, rõ ràng là chức năng của bạn không trong sạch, tuy nhiên, tại sao println (), như được định nghĩa trong scala, không thuần túy
aName

1
@aName Bởi vì ngoài giá trị trả về, ()nó còn biến đổi trạng thái không cục bộ trong System.out.
Mario Galic

Tôi nghĩ rằng thực tế quan trọng còn thiếu trong câu trả lời này là println thêm một ký tự dòng mới vào đầu vào.
Federico S

4

Các tác dụng phụ là trong trạng thái của máy tính. Mỗi lần bạn gọi println()trạng thái thay đổi bộ nhớ để hiển thị giá trị đã cho cho thiết bị đầu cuối. Hay nói chung hơn, trạng thái của luồng đầu ra tiêu chuẩn được thay đổi.


1
Một phần đúng, thực hiện bất kỳ hoạt động nào sẽ là trạng thái của bộ đếm lệnh, do đó, bất cứ điều gì cũng là một tác dụng phụ. Định nghĩa về hiệu ứng phụ xuất phát từ định nghĩa về tính minh bạch tham chiếu, mà nhiều người định nghĩa về mặt sửa đổi đối với trạng thái có thể thay đổi được chia sẻ.
Luis Miguel Mejía Suárez

2
theo cách này, mọi chức năng, hoạt động .... sẽ không trong sạch, vì nó thay đổi, trạng thái của bộ nhớ cpu .....,
aName

2

Câu trả lời hay đã được đưa ra cho câu hỏi này, nhưng hãy để tôi thêm hai xu của tôi.

Nếu bạn sẽ xem bên trong printlnhàm về cơ bản thì nó giống như java.lang.System.out.println()- vì vậy khi bạn gọi printlnphương thức thư viện chuẩn của Scala dưới mui xe, nó sẽ gọi phương thức printlntrên PrintStreamthể hiện đối tượng được khai báo là trường outtrong Systemlớp (hay chính xác hơn là outVartrong Consoleđối tượng), nó thay đổi trạng thái bên trong . Điều này có thể được coi là một giải thích khác tại sao printlnlà chức năng không tinh khiết.

Hi vọng điêu nay co ich!


1

Nó phải làm với khái niệm minh bạch tham chiếu . Một biểu thức được minh bạch tham chiếu nếu bạn có thể thay thế nó bằng kết quả được đánh giá mà không thay đổi chương trình .

Khi một biểu thức không minh bạch về mặt tham chiếu, chúng tôi nói rằng nó có tác dụng phụ .

f(println("effect"), println("effect"))
// isn't really equivalent to!
val x = println("effect")
f(x, x)

trong khi

import cats.effect.IO

def printlnIO(line: String): IO[Unit] = IO(println(line))

f(printlnIO("effect"), printlnIO("effect"))
// is equivalent to
val x = printlnIO("effect")
f(x, x)

Bạn có thể tìm thấy lời giải thích chi tiết hơn tại đây: https://typelevel.org/blog/2017/05/02/io-monad-for-cats.html


Tôi không thấy lý do tại sao f (x, x) khác với f (println ("effect"), println ("effect")) !!
aName

f(println("effect"), println("effect"))sẽ in hai lần trong "hiệu ứng" của bàn điều khiển trong khi val x = println("effect");f(x,x)sẽ in một lần.
Didac Montero

định nghĩa của hàm f
aName
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.