【问题标题】:Why is println considered an impure function?为什么 println 被认为是不纯函数?
【发布时间】:2020-06-09 16:47:48
【问题描述】:

我正在阅读 scala 编程的书,据说:

...在这种情况下,它的副作用是打印到标准输出流。

我看不出副作用在哪里,因为对于相同的输入,println 将打印相同的输出(我认为)
更新
例如,任何时候我们打电话:

println(5)

它会打印 5,我没有看到调用 println(5) 会打印 5 以外的值的情况!!

【问题讨论】:

标签: scala functional-programming pure-function


【解决方案1】:

这与参照透明度的概念有关。如果您可以在不更改程序的情况下将其替换为计算结果,则表达式是引用透明的。

当一个表达式在引用上不透明时,我们说它有副作用

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")) 不同!!
  • f(println("effect"), println("effect")) 将在控制台“效果”中打印两次,而 val x = println("effect");f(x,x) 将打印一次。
  • 函数f的定义是什么
【解决方案2】:

您可以通过将表达式替换为其结果来判断表达式是否具有副作用。如果程序改变了含义,就会产生副作用。例如,

println(5)

是不同的程序

()

也就是说,副作用是任何可观察到的效果,但未编码在计算表达式的结果中。这里的结果是(),但该值中没有任何内容表明 5 现在出现在屏幕上的某个位置。

【讨论】:

  • 实际上,这并不是对“副作用” 的良好定义——副作用可以定义为任何破坏参照透明度的事物。你试图在这里展示的是 RT,但你的例子是错误的。由于多次执行某事应该多次执行相同的操作 - 相反,val a = println("hello"); val b = (a, a) 应该与 val b = (pritnln("hello"), println("hello")) 相同。
  • @LuisMiguelMejíaSuárez 也许我的例子不清楚,但我不认为这是错误的。我基本上是在指出程序println(5)() 之间的区别。还是你的意思是最后一句话?
  • 是的,但您并不清楚。因为,正如我所说,问题不在于多次调用某个东西,所以问题在于用它的定义替换引用是否会产生这种影响。
  • 我会说这是错误的,因为某些东西完全有可能产生副作用并且是幂等的,因此重复它不会改变效果。例如。赋值给可变变量;你怎么区分x = 1x = 1; x = 1; x = 1
  • @AlexeyRomanov 我已经编辑了我的答案。但是对于您的示例,我看不出第二个和第三个作业是如何产生副作用的。那里什么都没有改变。
【解决方案3】:

这个问题已经给出了很好的答案,但让我加两分钱。

如果您查看 println 函数的内部,它本质上与 java.lang.System.out.println() 相同 - 因此,当您在底层调用 Scala 的标准库 println 方法时,它会在 PrintStream 对象实例上调用方法 printlnSystem 类中声明为字段out(或更准确地说是Console 对象中的outVar),这会改变它的内部状态。这可以认为是println是不纯函数的另一种解释。

希望这会有所帮助!

【讨论】:

    【解决方案4】:

    考虑以下类比

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

    这里myprintln 是不纯的,因为除了返回值() 之外,它还会改变非局部变量out 作为副作用。现在想象out 是流香草println 发生变异。

    【讨论】:

    • 谢谢你的回复,很明显你的函数是不纯的,但是为什么scala中定义的println()不是纯的
    • @aName 因为除了返回值() 之外,它还会改变System.out 中的非本地状态。
    • 我认为这个答案中缺少的关键事实是 println 在输入中添加了换行符。
    【解决方案5】:

    副作用是电脑的状态。每次调用println() 时,内存状态都会发生变化,以便向终端显示给定值。或者更一般地说,标准输出流的状态发生了变化。

    【讨论】:

    • 部分正确,执行任何操作都会影响指令计数器的状态,因此任何事情都是副作用。副作用的定义源自引用透明性的定义,许多人根据对共享可变状态的修改来定义它。
    • 这样,任何函数,操作....都将是不纯的,因为它改变了,内存cpu的状态......,
    猜你喜欢
    • 2016-04-22
    • 2020-06-18
    • 1970-01-01
    • 2012-08-08
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    相关资源
    最近更新 更多