草庐IT

lambda - 范围函数 apply/with/run/also/let : Where do their names come from?

coder 2023-05-08 原文

关于标准库函数apply/with/run/的用法的博文(如this)有不少。 also/let available 以便更容易区分何时实际使用这些漂亮功能中的哪一个。

几周以来,官方文档甚至最终提供了关于该主题的指南:https://kotlinlang.org/docs/reference/coding-conventions.html#using-scope-functions-applywithrunalsolet

不过,我认为通过函数名称记住函数的各个用例是相当困难的。我的意思是,对我来说它们似乎是可以互换的,例如为什么不将 let 称为 run

有什么建议吗?我认为这些名字不是很有表现力,所以一开始很难看出它们的区别。

最佳答案

这里是一个非官方概述这些名字的由来。

let 受到函数式编程世界的启发。根据Wikipedia

a "let" expression associates a function definition with a restricted scope

在像 Haskell 这样的 FP 语言中,您可以使用 let像这样将值绑定(bind)到受限范围内的变量

aaa = let y = 1+2
          z = 4+6
          in  y+z

Kotlin 中的等效代码(尽管过于复杂)是

fun aaa() = (1+2).let { y -> 
              (4+6).let { z ->
                y + z
              } 
            }

let 的典型用法就是将一些计算的结果绑定(bind)到一个作用域而不“污染”外部作用域。

creater.createObject().let {
    if (it.isCorrect && it.shouldBeLogged) {
        logger.log(it)
    }
}

// `it` is out of scope here

with 函数的灵感来自 with语言结构,如 DelphiVisual Basic (可能还有很多其他人)在哪里

The with keyword is a convenience provided by Delphi for referencing elements of a complex variable, such as a record or object.

myObject.colour := clRed;
myObject.size   := 23.5;
myObject.name   := 'Fred';

can be rewritten :

with myObject do
begin
  colour := clRed;
  size   := 23.5;
  name   := 'Fred';
end;

等效的 Kotlin 将是

with(myObject) {
    color = clRed
    size = 23.5
    name = "Fred"
}

申请

apply 在里程碑阶段(M13)相对较晚的时间才将其添加到标准库中。可以看this来自 2015 年的问题,用户确切地要求这样的功能,甚至建议后来使用的名称“应用”。

在问题 https://youtrack.jetbrains.com/issue/KT-6903https://youtrack.jetbrains.com/issue/KT-6094你可以看到关于命名的讨论。 build 等替代方案和 init被提议但名称 apply由 Daniil Vodopian 提出,最终获胜。

apply类似于 with因为它可以用于在构造函数之外初始化对象。这就是为什么,在我看来,apply还不如命名为with .然而作为 with首先被添加到标准库中,Kotlin 开发人员决定不破坏现有代码并将其添加到不同的名称。

具有讽刺意味的是,Xtend 语言提供了所谓的 with-operator => apply 基本相同.

还有

also 甚至晚于 apply 才被添加到标准库中,即在 1.1 版中。再次,https://youtrack.jetbrains.com/issue/KT-6903包含讨论。功能基本类似于apply除了它需要一个常规的 lambda (T) -> Unit而不是扩展 lambda T.() -> Unit .

建议的名称包括“applyIt”、“applyLet”、“on”、“tap”、“touch”、“peek”、“make”。但是“also”赢了,因为它不会与任何关键字或其他 stdlib 函数发生冲突,而且它的用法(或多或少)读起来像英文句子。

例子

val object = creater.createObject().also { it.initiliaze() }

有点像

Creater, create the object and also initialize it!

其他 stdlib 函数的用法有点像英文句子,包括 takeIf takeUnless 在 1.1 版中也添加了这些内容。

运行

最后, run 函数实际上有两个签名。第一个fun <R> run(block: () -> R): R只需要一个 lambda 并运行它。它主要用于将 lambda 表达式的结果分配给顶级属性

val logger = run {
    val name = System.property("logger_name")
    Logger.create(name)
}

第二个签名fun <T, R> T.run(block: T.() -> R): R是一个扩展函数,它以扩展 lambda 作为参数,并且出于对称原因似乎也被命名为“运行”。它还“运行”一个 lambda,但在扩展接收器的上下文中

val result = myObject.run {
    intitialize()
    computeResult()
}

我不知道命名的任何历史原因。

关于lambda - 范围函数 apply/with/run/also/let : Where do their names come from?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48218400/

有关lambda - 范围函数 apply/with/run/also/let : Where do their names come from?的更多相关文章

  1. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

    这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

  2. ruby-on-rails - Ruby on Rails with Haml - 如何从 erb 切换 - 2

    我正在从erb文件切换到HAML。我将hamlgem添加到我的系统中。我创建了app/views/layouts/application.html.haml文件。我应该只删除application.html.erb文件吗?此外,仍然有/public/index.html文件被呈现为默认页面。我想创建自己的默认index.html.haml页面。我应该把它放在哪里以及如何使系统呈现该文件而不是默认索引文件?谢谢! 最佳答案 是的,您可以删除任何已转换为HAML的View的ERB版本。至于你的另一个问题,删除public/index/h

  3. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  4. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  5. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  7. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  8. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  9. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  10. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

随机推荐