在实践中,我也建议使用过滤器,因为它更紧凑,而且不神秘。
fichier.filter {case (name, age) => (age %2 == 0)}
由于 RHS 上不使用名称(右手边,大多数情况下:等号右侧,这里:大小写表达式的右侧)),我们可以指出:
fichier.filter {case (_, age) => (age %2 == 0)}
更紧凑的是使用位置值,但它是更少的自我记录。与其他所有具有位置的元素(如 List、Vector、String ...)相比,计数从 1 开始,而不是 0。
fichier.filter (_._2 %2 == 0)
但是,您提到的练习是自己实施,这是一个合理的学习过程。那么为什么不一劳永逸地自己实现过滤器呢?
让我们开始具体的,例如,仅过滤具有第二个元素 int 的 touples。
停止阅读,先尝试自己。
在这种情况下,过滤器是什么?
fichier.filter {case (name, age) => (age %2 == 0)}
它对异类元组列表进行操作,但我们现在不喜欢由亚当和夏娃开始,所以让我们将列表作为附加参数。编写一个基本的 List 实现也不会太难,但是现在......我们从一个特定的元组开始,逐步使其更通用。该列表是一个元组列表,该函数接受一个 Int 并返回一个布尔值。一个接受 Int 并返回 Boolean 的函数如下所示: f: (Int => Boolean))
scala> def tfilter (tl: List [(String, Int)], f: (Int => Boolean)) : List[(String, Int)] = tl match {
| case Nil => Nil
| case t :: tt => if (f (t._2)) t :: tfilter (tt, f) else tfilter (tt, f)
| }
tfilter: (tl: List[(String, Int)], f: Int => Boolean)List[(String, Int)]
这个方法有点空。它接受 List,获取第一个元素 t,用 f(t._2) 对其进行测试(我们知道元组的第二部分是一个 Int,对于给定的 Int,函数返回 true 或 false。如果为 true,则返回元素和列表的其余部分,tt,以相同的方式过滤。因此我们必须传递函数。
调用:
scala> tfilter (fichier, (_ % 2 == 0))
res107: List[(String, Int)] = List((Bill Morton,36), (Georgia Bates,30), (Jesse Caldwell,46), (Jeffery Wolfe,50), (Roy Norris,18), (Ella Gonzalez,48))
内括号定义了我们的函数。它是一个占位符,由函数填充,因为它由一个参数调用,并返回该值的模 2 是否等于 0。这里有两个符号变体。
tfilter (fichier, (i:Int => i % 2 != 0))
tfilter (fichier, ((i:Int) => i % 2 != 0))
但是,如果我们的下一个元组是相反的,并且 int 在第二个位置呢?位置 1 在我们的方法中是硬编码的,这是一件坏事。
再一次,先试试自己。改变参数的顺序很复杂。我们最好立即参数化我们期望的类型,因此,我们返回什么,因此,谓词,返回布尔值的函数期望什么。
我们只是将元组 (String, Int) 命名为 X:
scala> def tfilter (tl: List [X], f: (X => Boolean)) : List[X] = tl match {
| case Nil => Nil
| case t :: tt => if (f (t)) t :: tfilter (tt, f) else tfilter (tt, f)
| }
<console>:8: error: not found: type X
def tfilter (tl: List [X], f: (X => Boolean)) : List[X] = tl match {
^
标题中的3个更改,以及测试f(t)中的一个更改。但 REPL 并不高兴。
我们必须对整个方法进行Type注解:
scala> def tfilter [X] (tl: List [X], f: (X => Boolean)) : List[X] = tl match {
| case Nil => Nil
| case t :: tt => if (f (t)) t :: tfilter (tt, f) else tfilter (tt, f)
| }
tfilter: [X](tl: List[X], f: X => Boolean)List[X]
现在 REPL 很高兴。无论 X 是什么,我们都承诺传递一个 X 列表和一个方法,该方法可以将单个 X 转换为布尔值,我们将返回一个 X 列表。
我们现在可以逐步进行。编写一个方法,它接受一个元组和一个函数 Int => Boolean,并在元组的 Int 部分(这里是第 2 部分)上执行函数:
def tuple2bool (si: (String, Int), f: (Int => Boolean)) : Boolean = f (si._2)
非常琐碎,除了未使用的符号。单个元组的小测试:
tuple2bool (fichier(0), (i:Int) => (i % 2 == 0))
Boolean = false
tuple2bool (fichier(5), (i:Int) => (i % 2 == 0))
Boolean = true
我们如何在整个列表中调用它?
好吧,首先我们介绍两个参数列表的概念。
def (a: Int, b:Int, c:Int) = ...
和
差不多
def (a: Int) (b:Int, c:Int) = ...
函数可以对a,b,c里面进行操作。但是,对于implicits,只能隐含整个参数列表,因此如果我们只想隐含一些元素,我们将它们分组到自己的参数列表中。对于编译器和编码器来说,更容易弄清楚遗漏了什么。
在这里,对我来说,在不同的参数列表中分离 List 和函数调用也更容易:
def tfilter [X] (tl: List [X]) (f: (X => Boolean)) : List[X] = tl match {
case Nil => Nil
case t :: tt => if (f (t)) t :: tfilter (tt)(f) else tfilter (tt)(f)
}
使用两个参数列表进行函数调用有点可怕,但要么你很少这样做,要么你很快就习惯了。现在,让我们调用我们的
tfilter (fichier) (si => tuple2bool (si, _ % 2 == 0 ))
List[(String, Int)] = List((Bill Morton,36), (Georgia Bates,30), (Jesse Caldwell,46), (Jeffery Wolfe,50), (Roy Norris,18), (Ella Gonzalez,48))
第一个参数是我们的 fichier,第二个是一个函数,它接受一个 si,一个绑定我们的 tuple2bool 参数 1 的标识符,然后是在 Int 上执行的函数,它将被提取。 _ 是那个未绑定的 Int 的占位符。
但我们不想一遍又一遍地编写这样的中间函数。我们可以马上写:
tfilter (fichier) (si => (si._2 % 2 == 0 ))
List[(String, Int)] = List((Bill Morton,36), (Georgia Bates,30), (Jesse Caldwell,46), (Jeffery Wolfe,50), (Roy Norris,18), (Ella Gonzalez,48))
完全忘记 tuple2bool-fun。
而且我们现在非常灵活。如果我们决定编写一个 Personne 类,并更改订单年龄/姓名:
val personnes: List [Personne] = List (Personne(21, "Emma Jacobs"), Personne(54, "Mabelle Bradley"))
personnes: List[Personne] = List(Personne(21,Emma Jacobs), Personne(54,Mabelle Bradley))
tfilter (personnes) (p => (p.age % 2 == 0 ))
List[Personne] = List(Personne(54,Mabelle Bradley))
我减少了列表以获得更小的结果,并改变了 Mabelle 的年龄,以对过滤产生真正的影响。
我们现在不仅可以使用 int 函数进行过滤:
tfilter (personnes) (p => (p.name.contains ('a')))
List[Personne] = List(Personne(21,Emma Jacobs), Personne(54,Mabelle Bradley))
使用 fichier,它的工作方式相同:
tfilter (fichier) (p => (p._1.contains ('a')))
我们可以嵌套我们的函数,通过第二个过滤器传递元组:
tfilter (tfilter (fichier) (p => (p._1.contains ('a'))))(p => p._2 < 30)
List[(String, Int)] = List((Emma Jacobs,21))
The original, sequential style in the scala libs, is surely more elegant, better to parse for us coders, mainly:
fichier.filter (p => (p._1.contains ('a'))).filter (p => p._2 < 30)
至少,我们可以采用前面介绍的语法,用'case':
tfilter (fichier)({case (name, _) => (name.length > 13)})