解释一下 Scala 中 Y 组合器的实现?

2024-05-22

这是 Y 组合器在 Scala 中的实现:

scala> def Y[T](func: (T => T) => (T => T)): (T => T) = func(Y(func))(_:T)
Y: [T](func: (T => T) => (T => T))T => T

scala> def fact = Y {
     |           f: (Int => Int) =>
     |             n: Int =>
     |               if(n <= 0) 1
     |               else n * f(n - 1)}
fact: Int => Int

scala> println(fact(5))
120

Q1:结果如何120一步步走出来?因为Y(func)定义为func(Y(func)),Y应该变得越来越多,Y在哪里丢失了,怎么样了120在peform过程中出来吗?

Q2: 有什么区别

def Y[T](func: (T => T) => (T => T)): (T => T) = func(Y(func))(_:T)

and

def Y[T](func: (T => T) => (T => T)): (T => T) = func(Y(func))

它们在scala REPL中是相同的类型,但是第二个无法打印结果120?

scala> def Y[T](func: (T => T) => (T => T)): (T => T) = func(Y(func))
Y: [T](func: (T => T) => (T => T))T => T

scala> def fact = Y {
     |           f: (Int => Int) =>
     |             n: Int =>
     |               if(n <= 0) 1
     |               else n * f(n - 1)}
fact: Int => Int

scala> println(fact(5))
java.lang.StackOverflowError
  at .Y(<console>:11)
  at .Y(<console>:11)
  at .Y(<console>:11)
  at .Y(<console>:11)
  at .Y(<console>:11)

首先,请注意,这不是Y-组合器,因为该函数的 lambda 版本使用自由变量 Y。不过,它是 Y 的正确表达式,只是不是组合器。

因此,我们首先将计算阶乘的部分放入一个单独的函数中。我们可以称其为comp:

def comp(f: Int => Int) =
  (n: Int) => {
    if (n <= 0) 1
    else n * f(n - 1)
  }

现在可以像这样构造阶乘函数:

def fact = Y(comp)

Q1:

Y 定义为 func(Y(func))。我们调用fact(5),它实际上是Y(comp)(5),并且Y(comp)计算结果为comp(Y(comp))。这就是关键点:我们停在这里因为 comp 需要一个函数并且它直到需要时才对其进行评估。因此,运行时将 comp(Y(comp)) 视为 comp(???) 因为 Y(comp) 部分是一个函数,并且仅在需要时才会进行计算。

你知道 Scala 中的按值调用和按名称调用参数吗?如果您将参数声明为someFunction(x: Int),一旦调用 someFunction 就会对其进行求值。但如果你将其声明为someFunction(x: => Int),那么 x 不会立即求值,而是在使用它的时候求值。第二个调用是“按名称调用”,它基本上将 x 定义为“不接受任何内容并返回 Int 的函数”。因此,如果您传入 5,实际上是传入一个返回 5 的函数。通过这种方式,我们可以实现函数参数的惰性求值,因为函数是在使用时进行求值的。

因此,comp 中的参数 f 是一个函数,因此仅在需要时才对其进行求值,即位于 else 分支中。这就是为什么整个事情有效 - Y 可以创建一个无限的 func(func(func(func(…)))) 链,但该链是惰性的。仅在需要时才计算每个新链接。

因此,当您调用fact(5)时,它将穿过主体进入else分支并仅在该点 f 才会被评估。以前没有。由于 Y 传入 comp() 作为参数 f,我们将再次深入探讨 comp()。在 comp() 的递归调用中,我们将计算 4 的阶乘。然后,我们将再次进入 comp 函数的 else 分支,从而有效地进入另一级递归(计算 3 的阶乘)。请注意,在每个函数调用中,您的 Y 提供了一个 comp 作为 comp 的参数,但它仅在 else 分支中进行评估。一旦我们到达计算阶乘 0 的级别,if 分支将被触发,我们将停止进一步向下潜水。

Q2:

This

func(Y(func))(_:T)

是这个的语法糖

x => func(Y(func))(x)

这意味着我们将整个事情包装到一个函数中。我们这样做并没有失去任何东西,反而得到了。

我们得到了什么?嗯,这和上一个问题的答案是一样的;这样我们就可以实现这一点func(Y(func))仅在需要时才会评估,因为它包装在函数中。这样我们就可以避免无限循环。将(单参数)函数 f 展开为函数 x => f(x) 称为eta 扩展(你可以阅读更多相关内容here https://medium.com/@sinisalouc/on-method-invocations-or-what-exactly-is-eta-expansion-1019b37e010c#.u5gmoh126).

这是 eta 扩展的另一个简单示例:假设我们有一个方法getSquare()它返回一个简单的square()函数(即计算数字平方的函数)。我们可以返回一个接受 x 并返回 square(x) 的函数,而不是直接返回 square(x):

def square(x: Int) = x * x
val getSquare: Int => Int = square
val getSquare2: Int => Int = (x: Int) => square(x)

println(square(5)) // 25
println(getSquare(5)) // 25
println(getSquare2(5)) // 25

希望这可以帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解释一下 Scala 中 Y 组合器的实现? 的相关文章

  • 如何在Dotty中使用given?

    我在看Dotty下的文档Contextual Abstractions页面 我看到了Given Instances 给定实例 或者简单地 给定 定义了 规范 值 用于合成给定子句的参数的某些类型 例子 trait Ord T def com
  • IntelliJ IDEA Scala 插件问题

    我对新的 Intellij IDEA 10 和 Scala 插件有疑问 当我在 Scala 源文件中输入任何内容时 编辑器会永久冻结 在其他文件 java 和其他 编辑器中效果很好 结构视图 scala 检查和显示成员功能已关闭 堆大小增加
  • 了解如何使用 apply 和 unappy

    我试图更好地理解 的正确用法apply and unapply方法 考虑到我们想要序列化和反序列化的对象 这是正确的用法吗 即斯卡拉方式 的使用apply and unapply case class Foo object Foo appl
  • 重塑案例类构造函数?

    试图找到一种方法来 重塑 案例构造函数以填充某些默认值 以下情况可能吗 def reshape T R1 lt HList R2 lt HList h R1 R2 gt T example case class MyClass a Doub
  • 对于空列表,max() 应该返回什么?

    Got java util NoSuchElementException head of empty list所以我试着检查一下 但现在我明白了 info max of a few numbers FAILED info 0 did not
  • Scala 中值类的隐式 Json 格式化程序

    我有许多值类组成了一个更大的对象案例类 final case class TopLevel foo Foo bar Bar final case class Foo foo String extends AnyVal final case
  • Scala Array.apply 有何魔力

    来自 scala 2 10 4 的 array scala Array定义为 final class Array T length Int extends java io Serializable with java lang Clonea
  • 最小重复子串

    我正在看 Perl代码高尔夫页面 http www perlmonks org node id 82878 不要问为什么 并遇到了这个 第 3 洞 最小重复图案 编写一个子例程 它接受一个字符串 该字符串可能包含 重复模式 并返回最小的重复
  • 如何抑制spark输出控制台中的“Stage 2===>”?

    我有数据帧并试图获取不同的计数并且能够成功获取不同的计数 但是每当 scala 程序执行时我都会收到此消息 Stage 2 gt 1 1 2 我如何在控制台中抑制特定的此消息 val countID dataDF select substr
  • 如何从 SparkSQL DataFrame 中的 MapType 列获取键和值

    我的镶木地板文件中有数据 该文件有 2 个字段 object id String and alpha Map lt gt 它被读入 SparkSQL 中的数据帧 其架构如下所示 scala gt alphaDF printSchema ro
  • 通用特征的隐式转换

    我正在实现一个数据结构 并希望用户能够使用任何类型作为密钥 只要他提供一个合适的密钥类型来包装它 我有这个关键类型的特质 这个想法是进行从基类型到键类型的隐式转换 反之亦然 实际上 只使用基类型 该特征看起来像这样 trait Key T
  • Scala:类似 Option (Some, None) 但具有三种状态:Some、None、Unknown

    我需要返回值 当有人询问值时 告诉他们以下三件事之一 这是值 没有价值 我们没有关于该值的信息 未知 情况 2 与情况 3 略有不同 示例 val radio car radioType 我们知道该值 返回无线电类型 例如 pioneer
  • 为什么《Scala 中的函数式编程》一书的“无异常处理错误”一章中没有提到“scala.util.Try”?

    在 Scala 中的函数式编程 一书中的 无异常处理错误 一章中 作者给出 从函数体抛出异常的问题 Use Option如果我们不关心实际的异常 Use Either如果我们关心实际的异常 But scala util Try没有提到 从我
  • Scala Tuple2Zipped 与 IterableLike zip

    两种实现有什么区别 这个比那个好吗 有一篇博客文章说 Tuple2Zipped 性能更好 但没有提供原因 并且查看源代码我没有看到差异 val l1 List 1 2 3 val l2 List 5 6 7 val v1 l1 zip l2
  • Java 中的“Lambdifying”scala 函数

    使用Java和Apache Spark 已用Scala重写 面对旧的API方法 org apache spark rdd JdbcRDD构造函数 其参数为 AbstractFunction1 abstract class AbstractF
  • 类型级编程有哪些示例? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我不明白 类型级编程 是什么意思 也无法使用Google找到合适的解释 有人可以提供一个演示类型级编程的示例吗 范式的解释和 或定义将
  • Scala 中的 Shapeless 结构编程:如何正确使用 SYB 实现?

    我想使用SYB http research microsoft com en us um people simonpj papers hmap 实施于无形图书馆 https github com milessabin shapeless编写
  • 在 IntelliJ 中运行 Spark 字数统计

    我花了几个小时浏览 You Tube 视频和教程 试图了解如何在 Scala 中运行 Spark 字数统计程序 并将其转换为 jar 文件 我现在完全糊涂了 我运行了 Hello World 并且了解了如何在 Apache spark sp
  • Scalatest PlusPlay Selenium 无法调整窗口大小

    对此已经研究了一段时间 我似乎找不到使用 scalatest plus 调整窗口大小的方法 我发现在线搜索或文档的唯一方法http doc scalatest org 2 1 5 index html org scalatest selen
  • 如何定义与更高类型类型(类型构造函数)绑定的上下文

    我尝试过以下方法 def test Option T Ordering value1 Option T value2 Option T val e implicitly Ordering Option T compare value1 va

随机推荐