为什么在scala中创建List时我们需要Nil?

时间:2016-06-10 06:42:58

标签: list scala

我在列表上有一个基本问题

当我尝试使用cons运算符

创建List时,我收到以下错误
scala> val someList = 1::2
<console>:10: error: value :: is not a member of Int
   val someList = 1::2
                   ^

但是如果你看下面的话,我一旦加上Nil就行了..

    scala> val someList = 1::2::Nil
    someList: List[Int] = List(1, 2)

我想知道为什么在我们创建列表时至少需要一次Nil

Nil是数据类型吗?还是空元素?

4 个答案:

答案 0 :(得分:11)

正是因为这个原因。

  

value ::不是Int

的成员

在Scala中,运算符实际上是对象的函数。在这种情况下,::Nil对象上的函数,实际上是一个空列表对象

scala> Nil
res0: scala.collection.immutable.Nil.type = List()

当您执行1::2时,Scala会在::上查找名为2的函数,但它找不到该函数。这就是它失败的原因。

注意:在Scala中,如果运算符的最后一个字符不是冒号,则在第一个操作数上调用运算符。例如,1 + 2基本上是1.+(2)。但是,如果最后一个字符是冒号,则在右侧操作数上调用该运算符。因此,在这种情况下,1 :: Nil实际上是Nil.::(1)。由于::会返回另一个列表对象,因此您可以将其链接起来,就像1 :: 2 :: Nil实际上是Nil.::(2).::(1)一样。

答案 1 :(得分:7)

名称以:结尾的中缀运算符被解释为操作数上的方法调用。因此1 :: 22.::(1),即它调用::上的方法2。同样,1 :: 2 :: NilNil.::(2).::(1)

第一个不起作用的原因是2IntInt没有::方法。第二个工作的原因是Nil是一个列表,列表确实有::方法。由于List.::的结果也是一个列表,您仍然可以对第一个::的结果调用::

答案 2 :(得分:2)

Nil是将List创建为递归数据结构的基本构建块。 List是有用的数据结构,它为头部(第一个元素)提供恒定的时间访问O(1)。

列出它的最小核心,建立在3个操作之上 headtailisEmptyNilList的单例子类,因此它是一个特殊的类似实例,表示空列表缺点运算符::在List上定义,通过在List中预先添加一个元素来递归地构建列表。

参见trait List和object Nil(高度简化)

的定义
trait List[A] {
    def head: A
    def tail: List[A]
    def isEmpty: Boolean
}
case object Nil extends List[Nothing] {
    def head = throw new NoSuchElementException("head of empty list")
    def tail = throw new UnsupportedOperationException("tail of empty list")
    def isEmpty = true
}

由于任何标识符/运算符以:结尾,与右侧相关联,::运算符也与右侧相关联。

当您编写1::2::3时,scala会尝试将此调用重写为3.::(2.::(1))。即3成为第一次调用::的接收者,它不存在于任何任意数据类型(在这种情况下为Int)。

这就是为什么你在空列表上总是建立 - Nil。可以把它想象为在空列表Nil上逐个添加每个元素。

答案 3 :(得分:1)

Scala中以冒号:结尾的所有运算符都在右操作数上定义。 因此,当您撰写1::2时,::意味着应2IntNilList。{/ 1}

::1 :: 2 :: Nil的值,并在其上定义了(Nil.::(2)).::(1)的方法。因此,当您编写public class MyAccessDeniedHandler implements AccessDeniedHandler { private AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl(); public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { //Some CSRF related code // Then call accessDeniedHandlerImpl.handle to handle request accessDeniedHandlerImpl.handle(request, response, accessDeniedException); } /** * The error page to use. Must begin with a "/" and is interpreted relative to the current context root. * * @param errorPage the dispatcher path to display * * @throws IllegalArgumentException if the argument doesn't comply with the above limitations * @see AccessDeniedHandlerImpl#setErrorPage(String) */ public void setErrorPage(String errorPage) { // You can set custom error page here accessDeniedHandlerImpl.setErrorPage(errorPage); } } 时,它会被评估为render: function() { var modalView = <Button onPress={this.openModal3} style={styles.btn}>Position centered + backdrop + disable</Button> <Modal style={[styles.modal, styles.modal3]} position={"center"} ref={"modal3"} isDisabled={this.state.isDisabled}> <Text style={styles.text}>Modal centered</Text> <Button onPress={this.toggleDisable} style={styles.btn}>Disable ({this.state.isDisabled ? "true" : "false"})</Button> </Modal> </View> return ( <NavigatorIOS style ={{flex:1}} ref={'nav'} initialRoute={{ component: ()=> modalView, title:'Photoos.Net', passProps:{ name: this.props.title}, }}/> ); } });