使用Scala进行函数式编程练习

使用Scala进行函数式编程练习,scala,functional-programming,Scala,Functional Programming,我最近开始阅读Paul Chiusano和Rúnar Bjarnason的《Scala函数编程》一书,以此作为学习FP的一种方法。我想学习它,因为它会让我的头脑开阔一点,改变我的思维方式,也希望使我成为一名更好的程序员,至少我希望如此 在他们的书中,Chp。3、它们定义了一种基本的单链表类型,如下所示: package fpinscala.datastructures sealed trait List[+A] case object Nil extends List[Nothing] cas

我最近开始阅读Paul Chiusano和Rúnar Bjarnason的《Scala函数编程》一书,以此作为学习FP的一种方法。我想学习它,因为它会让我的头脑开阔一点,改变我的思维方式,也希望使我成为一名更好的程序员,至少我希望如此

在他们的书中,Chp。3、它们定义了一种基本的单链表类型,如下所示:

package fpinscala.datastructures

sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]

object List {
    def sum(ints: List[Int]): Int = ints match {
    case Nil => 0
    case Cons(x,xs) => x + sum(xs)
    }
    def product(ds: List[Double]): Double = ds match {
    case Nil => 1.0
    case Cons(0.0, _) => 0.0
    case Cons(x,xs) => x * product(xs)
    }
    def apply[A](as: A*): List[A] =
    if (as.isEmpty) Nil
    else Cons(as.head, apply(as.tail: _*))
}
我现在正致力于实现tail方法,它的工作原理与Scala库中定义的tail方法类似。我想这里的想法是,在列表对象中定义一个tail方法,他们称之为companion方法,然后在另一个文件(比如主文件)中正常调用它

到目前为止,我有:

def tail[A](ls: List[A]): List[A] = ls match {
    case Nil => Nil
    case Cons(x,xs) => xs
    }
然后我在另一个文件夹中创建了一个主文件:

package fpinscala.datastructures

object Main {
    def main(args:Array[String]):Unit = {
       println("Hello, Scala !! ")
       val example = Cons(1, Cons(2, Cons(3, Nil)))
    val example2 = List(1,2,3)
       val example3 = Nil
    val total = List.tail(example)
    val total2 = List.tail(example3)
    println(total2)
}
}
这很有效,给了我:

Hello, Scala !! 
Cons(2,Cons(3,Nil))
我的问题是:

这是否是编写tail方法的正确方法,可能是作者想要的?这个包结构正确吗?因为我觉得很不对,尽管我只是遵循作者的方案

我也不知道我是否应该使用一个特定的类型而不是编写一个多态方法(就是这个名字?)


请容忍我,因为我是FP艺术的新手。

看起来不错。那怎么办

def tail[A](xs: List[A]): List[A] = xs match {
    case Nil => Nil
    case head :: xxs => xxs
}

在默认的Scala列表实现中,尝试获取空列表的尾部将引发UnsupportedOperationException。所以你可能想要更像

def tail[A](ls: List[A]): List[A] = ls match {
    case Nil => throw new UnsupportedOperationException()
    case Cons(x,xs) => xs
}
此外,qantik建议使用::运算符的答案也适用于Scala的默认列表实现,但由于在这个自定义列表实现上没有定义::方法,因此它将不起作用

最后,您可能需要考虑定义尾部,以便不做

val list = List(1, 2, 3)
val restOfList = tail(list). 
你可以这样做

val list = List(1, 2, 3)
val restOfList = list.tail

这需要在列表特性上定义方法,而不是在列表对象中定义方法

对我来说似乎很好。尽管如此:1。你不想让犯人暴露在秘密特征之外,所以最好是保密的。为什么在主函数中需要示例和总变量?谢谢!我怎样才能“封装”犯人,使其成为私人的?我理解这个例子,但是改变实际的类对我来说还是有点太多了,在书中他们要求添加简单的方法,比如tail等等。。。所以我不知所措。关于这些例子,我想测试一下?