代码之家  ›  专栏  ›  技术社区  ›  Mike Spross Alex Martelli

Scala中正确的关联方法有什么好处?

  •  28
  • Mike Spross Alex Martelli  · 技术社区  · 17 年前

    我刚刚开始使用Scala,我刚刚了解了如何制作方法 右结合 (与更传统的 在命令式面向对象语言中常见)。

    起初,当我看到示例代码 cons 在Scala中,我注意到每个示例的右侧都有一个列表:

    println(1 :: List(2, 3, 4))
    newList = 42 :: originalList
    

    然而,即使在一遍又一遍地看到这一点之后,我也没有再三考虑,因为我当时并不知道这一点 :: List . 我只是假设它是一个操作符(同样,在Java中是操作符),而关联性并不重要。事实上 列表 在示例代码中总是出现在右边似乎是巧合(我认为这可能只是“首选样式”)。

    现在我更清楚了:必须这样写,因为 :: 是右关联的。

    我的问题是,能够定义正确的关联方法有什么意义?

    这纯粹是出于审美的原因,还是在某些情况下,右联想真的比左联想有某种好处?

    从我(新手)的角度来看,我真的不知道该怎么做

    1 :: myList
    

    有比这更好的吗

    myList :: 1
    

    3 回复  |  直到 17 年前
        1
  •  39
  •   VonC    17 年前

    简单的回答是,正确的关联性可以通过使程序员的类型与程序的实际操作保持一致来提高可读性。
    所以,如果你输入' 1 :: 2 :: 3
    那是因为 1 :: 2 :: 3 :: Nil "其实,

    List[Int].3.prepend(2).prepend(1)
    
    scala> 1 :: 2 :: 3:: Nil
    res0: List[Int] = List(1, 2, 3)
    

    这两者都是:

    • 更有效(O(1)用于 prepend ,vs.O(n)表示假设 append (方法)

    (提示,摘自本书) Programming in Scala )
    如果在运算符表示法中使用方法,例如 a * b ,在左操作数上调用该方法,如中所示 a.*(b) 除非方法名以冒号结尾。
    如果方法名以冒号结尾,则在右操作数上调用该方法。
    因此, 1 :: twoThree :: 方法在上调用 twoThree twoThree.::(1) .

    对于列表,它扮演附加操作的角色(列表似乎附加在要形成的“1”之后) 1 2 3 预先准备好的 添加到列表中)。
    类列表不提供真正的追加操作,因为追加到列表所需的时间随列表的大小线性增长,而 使用::预结束需要固定的时间 .
    myList :: 1 将尝试将myList的整个内容前置到“1”,这将比将1前置到myList的时间长(如中所示) 1 :: myList

    注意:但是,无论运算符具有何种关联性,其操作数都是 始终从左到右计算。

    { val x = a; b.:::(x) }
    

    在此块中,a仍在b之前进行评估,然后是此评估的结果 作为操作数传递给bs:::方法。


    它允许保持通常的左关联操作的外观 1::myList “)而实际对正确的表达式应用该操作,因为;

    • 效率更高。
    • 1::myList myList.prepend(1) ')


    注意,在以下情况下: foldLeft 例如,他们可能 gone a little to far (带’ /: '右关联运算符(等效)


    要包含您的一些评论,请稍作修改:

    如果你考虑一个“追加”函数,左关联,那么你会写 oneTwo append 3 append 4 append 5 '.

    如果是“append”,则与“::”相同。但事实并非如此。它实际上是“prepend”的意思

    这意味着 a :: b :: Nil “是为了” List[].b.prepend(a) '

    如果“::”是前置的,但仍保持左关联,则结果列表的顺序将错误。

    这是因为,您所做的将是,按照左关联顺序:

    (1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)
    

    因此,正确的关联性使代码与返回值的实际顺序匹配。

        2
  •  3
  •   Eugene Yokota    16 年前

    能够定义正确的关联方法有什么意义?

    我认为正确的关联方法的目的是给某人一个扩展语言的机会,这通常是操作符重写的目的。

    运算符重载是一件有用的事情,所以Scala说:为什么不将其开放给任何符号组合呢?相反,为什么要区分运算符和方法?现在,库实现者如何与内置类型交互,如 Int ? 在C++中,她会用到 friend 在全局范围内发挥作用。如果我们想要呢 全部的 类型来实现运算符 ::

    正确的关联性提供了一种干净的添加方法 运算符可用于所有类型。当然,从技术上来说 :: :: Nil 最后。

    我认为这反映了Scala的理念,即在库中实现尽可能多的东西,并使语言灵活地支持它们。这使某人有机会提出超级列表,可以称之为:

    1 :: 2 :: SuperNil
    

    有点不幸的是,正确的关联性目前只硬编码到结尾的冒号,但我想这使它很容易记住。

        3
  •  3
  •   CodePredator    12 年前

    def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldRight ys)(_::_)
    

    这个很好用。但不能使用foldLeft操作执行相同的操作

    def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldLeft ys)(_::_)
    

    ,因为 ::

    推荐文章