เหตุใด println จึงถือว่าเป็นฟังก์ชันที่ไม่บริสุทธิ์


10

ฉันกำลังอ่านการเขียนโปรแกรมหนังสือในรูปแบบสกาล่าและได้มีการกล่าวว่า:

... ในกรณีนี้ผลข้างเคียงของมันคือการพิมพ์ไปยังสตรีมเอาต์พุตมาตรฐาน

และฉันไม่เห็นว่าผลข้างเคียงอยู่ที่ใดเพราะสำหรับอินพุตเดียวกัน println จะพิมพ์เอาต์พุตเดียวกัน (ฉันคิดว่า)
อัปเดต
ทุกครั้งที่เราเรียกว่า:

println(5)

มันจะพิมพ์5ฉันไม่เห็นกรณีที่การโทรprintln(5)จะพิมพ์ค่าอื่นที่ไม่ใช่ 5 !!


หากนี่ตอบคำถามของคุณฉันจะลบคำตอบของฉันsoftwareengineering.stackexchange.com/q/40297/271736
joelb

3
คำตอบที่ความโปร่งใสอ้างอิงคืออะไร? ดูเหมือนเกี่ยวข้องที่นี่
Nathan Hughes

1
ที่เกี่ยวข้อง (lol) stackoverflow.com/q/4865616/5986907
joelb

2
คุณสับสนผลข้างเคียง (ไม่อ้างอิงโปร่งใส) กับ deterministic printlnเป็นฟังก์ชั่นที่กำหนดขึ้น แต่เพื่อความบริสุทธิ์จะต้องเป็น RT
บ๊อบ

2
เพราะมันทำอะไรอย่างอื่นนอกจากคำนวณผลลัพธ์แล้วคืนมัน
Seth Tisue

คำตอบ:


6

คุณสามารถบอกได้ว่านิพจน์มีผลข้างเคียงหรือไม่โดยการแทนที่นิพจน์ด้วยผลลัพธ์ หากโปรแกรมเปลี่ยนความหมายแสดงว่ามีผลข้างเคียง ตัวอย่างเช่น,

println(5)

เป็นโปรแกรมที่แตกต่างกันไป

()

นั่นคือผลข้างเคียงคือผลที่สังเกตได้ซึ่งไม่ได้เข้ารหัสในผลลัพธ์ของการประเมินการแสดงออก ผลลัพธ์คือที่นี่()แต่ไม่มีสิ่งใดในค่าที่เข้ารหัสความจริงที่ว่า 5 ได้ปรากฏขึ้นที่ใดที่หนึ่งบนหน้าจอของคุณ


6
ที่จริงแล้วไม่ใช่คำจำกัดความที่ดีของ"ผลข้างเคียง" - ผลข้างเคียงอาจถูกกำหนดให้เป็นสิ่งที่ทำให้เกิดความโปร่งใสในการอ้างอิง สิ่งที่คุณพยายามแสดงที่นี่คือ RT แต่ตัวอย่างของคุณผิด เนื่องจากการดำเนินการบางสิ่งบางอย่างหลาย ๆ ครั้งควรทำเช่นเดียวกันครั้งสิ่ง mutiple - แต่ควรจะเป็นเช่นเดียวกับval a = println("hello"); val b = (a, a) val b = (pritnln("hello"), println("hello"))
Luis Miguel MejíaSuárez

1
@ LuisMiguelMejíaSuárezบางทีตัวอย่างของฉันไม่ชัดเจน แต่ฉันไม่คิดว่ามันผิด ฉันเป็นหลักชี้ให้เห็นความแตกต่างระหว่างโปรแกรมและprintln(5) ()หรือคุณหมายถึงประโยคสุดท้าย?
joelb

ใช่ แต่คุณไม่ชัดเจนเกี่ยวกับเรื่องนี้ เนื่องจากที่ฉันกล่าวว่าปัญหาไม่ได้เรียกอะไรหลาย ๆ ครั้งปัญหาคือถ้าแทนที่การอ้างอิงด้วยคำนิยามของมันจะมีผลกระทบนั้น
Luis Miguel MejíaSuárez

ฉันไม่เข้าใจตัวอย่างของคุณชัดเจน
aName

5
ฉันว่ามันผิดเพราะเป็นไปได้อย่างสมบูรณ์แบบสำหรับบางสิ่งที่จะมีผลข้างเคียงและเป็น idempotent ดังนั้นการทำซ้ำจะไม่เปลี่ยนผลกระทบ เช่นการกำหนดให้กับตัวแปรที่ไม่แน่นอน คุณจะแยกแยะได้อย่างไรx = 1และx = 1; x = 1; x = 1?
Alexey Romanov

5

พิจารณาการเปรียบเทียบต่อไปนี้

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

นี่myprintlnคือสิ่งที่ไม่บริสุทธิ์เพราะนอกจากค่าที่ส่งคืน()แล้วมันยังทำการเปลี่ยนแปลงตัวแปรที่ไม่ใช่ในท้องถิ่นoutเป็นผลข้างเคียง ทีนี้ลองนึกภาพว่าoutกระแสวานิลลาจะprintlnกลายพันธุ์


1
ขอบคุณสำหรับการตอบกลับเป็นที่ชัดเจนว่าฟังก์ชั่นของคุณไม่บริสุทธิ์ แต่เหตุใด println () ตามที่กำหนดในส
กาลา

1
@aName เพราะนอกจากจะส่งคืนค่า()แล้วมันยังจะกลายเป็นสถานะที่ไม่ได้อยู่System.outภายใน
Mario Galic

ฉันคิดว่าข้อเท็จจริงสำคัญที่ขาดหายไปจากคำตอบนี้คือ println เพิ่มอักขระบรรทัดใหม่ให้กับอินพุต
Federico S

4

ผลข้างเคียงอยู่ในสถานะของคอมพิวเตอร์ ทุกครั้งที่คุณเรียกprintln()สถานะการเปลี่ยนแปลงของหน่วยความจำเพื่อแสดงค่าที่กำหนดให้กับเทอร์มินัล หรือโดยทั่วไปสถานะของสตรีมเอาต์พุตมาตรฐานจะเปลี่ยนไป


1
จริงบางส่วนดำเนินการใด ๆ ที่จะเป็นสถานะของเคาน์เตอร์คำสั่งดังนั้นสิ่งที่เป็นผลข้างเคียง คำจำกัดความของผลข้างเคียงสืบเนื่องมาจากคำจำกัดความของความโปร่งใสในการอ้างอิงซึ่งหลาย ๆ คนนิยามในแง่ของการปรับเปลี่ยนไปสู่สถานะที่ไม่แน่นอนที่ใช้ร่วมกัน
Luis Miguel MejíaSuárez

2
ด้วยวิธีนี้ฟังก์ชั่นใด ๆ การดำเนินงาน .... จะไม่บริสุทธิ์เนื่องจากมันเปลี่ยนสถานะของซีพียูหน่วยความจำ ..... ,
aName

2

ได้รับคำตอบที่ดีสำหรับคำถามนี้ แต่ให้ฉันเพิ่มอีกสองเซ็นต์

หากคุณจะมองเข้าไปในprintlnฟังก์ชั่นหลักก็เป็นเช่นเดียวกับjava.lang.System.out.println()- ดังนั้นเมื่อคุณเรียกห้องสมุด Scala มาตรฐานของprintlnวิธีการภายใต้ประทุนมันจะเรียกวิธีการprintlnในPrintStreamตัวอย่างวัตถุที่มีการประกาศเป็นเขตข้อมูลoutในSystemชั้นเรียน (หรืออย่างแม่นยำมากขึ้นoutVarในConsoleวัตถุ) ซึ่งเปลี่ยนแปลงรัฐภายใน . นี่ถือเป็นอีกคำอธิบายหนึ่งว่าทำไมprintlnฟังก์ชั่นที่ไม่บริสุทธิ์

หวังว่านี่จะช่วยได้!


1

มันจะทำอย่างไรกับแนวคิดของความโปร่งใสอ้างอิง การแสดงออกเป็น referentially โปร่งใสถ้าคุณสามารถแทนด้วยผลการประเมินของตนโดยไม่ต้องเปลี่ยนโปรแกรม

เมื่อการแสดงออกไม่โปร่งใส referentially เราบอกว่ามันมีผลข้างเคียง

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

ในขณะที่

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)

คุณสามารถดูคำอธิบายโดยละเอียดเพิ่มเติมได้ที่นี่: https://typelevel.org/blog/2017/05/02/io-monad-for-cats.html


ฉันไม่เห็นสาเหตุที่ f (x, x) แตกต่างจาก f (println ("effect"), println ("effect")) !!
aName

f(println("effect"), println("effect"))กำลังจะพิมพ์สองครั้งในคอนโซล "เอฟเฟกต์" ในขณะที่val x = println("effect");f(x,x)กำลังพิมพ์ครั้งเดียว
Didac Montero

คำจำกัดความของฟังก์ชั่น f
อะไร
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.