Scala - 在列表中查找特定元组

15

假设我们有这个元组列表:

val data = List(('a', List(1, 0)), ('b', List(1, 1)), ('c', List(0)))

该列表具有以下特征:

List[(Char, List[Int])]

我的任务是从“data”中查找键为“b”的元组内的“List[Int]”元素。如果我实现一个像“findIntList(data,'b')”这样的方法,我希望结果是List(1,1)。我尝试了以下方法:

  1. data.foreach{elem => if(elem._1==char) return elem._2}
  2. data.find(x=>x._1==ch)
  3. for(elem <- data) yield elem match {case (x,y:List[Bit])=>if(x==char) y}
  4. for(x <- data) yield if(x._1==char) x._2

使用所有的方法(除了第一种方法,在那里我使用显式的“return”),我得到的结果都是List[Option]List[Any],我不知道如何从中提取出“List[Int]”。


如果你有一个 Option(char, List[Bit]),比如从 data.find 得到的,你可以使用 .get 获取实际的元组:需要注意的是,如果没有找到任何内容,它会抛出异常。值得一提的是,在这个任务中,我只是编写了自己的函数,因为高阶列表函数只在第五周介绍。 - Mark Peters
(https://www.coursera.org/course/progfun) - Mark Peters
谢谢马克!这里的.get是关键。 - mainas
如果您有一个类型为“Option[T]”的元素,您可以通过map/foreach/filter等方式对其进行操作... 这作为对“null”的一种很好的抽象。 - Felix
作为@main,您应该接受解决问题的答案。只需单击答案旁边的复选标记即可。如果您在太多问题上不接受答案,人们将停止回答您的问题。 - Jens Schauder
抱歉,控制不住我的情绪...我同意...Scala 真是太可爱了 :) - human
5个回答

22

许多方式之一:

data.toMap.get('b').get

toMap 函数把由 2 元组组成的列表转换为一个以第一个元素为键,第二个元素为值的 Map。使用 get 方法可以通过给定的键获取对应的值,并返回一个 Option,因此您需要再次使用 get 方法来获取列表实际的值。

或者您可以使用:

data.find(_._1 == 'b').get._2 

注意:只有在能保证你将获得一个Some而不是None的情况下,才在Option上使用get。请参见http://www.scala-lang.org/api/current/index.html#scala.Option,了解如何使用Option的惯用法。

更新:解释您使用不同方法看到的结果类型

方法2: find返回一个Option [List [Int]],因为无法确保会找到匹配的元素。

方法3: 在这里,您基本上执行map,即将函数应用于集合中每个元素。对于您要查找的元素,该函数返回其List[Int],对于它包含的所有其他元素,它包含值(),该值是Unit值,大致相当于Java中的void,但是是实际的类型。由于´List[Int]´和´Unit´的唯一公共超类型是´Any´,因此您会得到´List[Any]´作为结果。

方法4 基本上与#3相同


1
@MarkPeters 我认为抛出那个异常是不好的风格(当然也不是惯用法)。所以我猜如果你实现了一些规定这种行为的API,并且你不能改变它,那么这样做就可以了。否则,请更改API以返回Option或Try(http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.util.Try),或者如果您喜欢Scalaz,则使用Validation(http://blog.lunatech.com/2012/03/02/validation-scala)。 - Jens Schauder
data.find(_._1 == 'b').get._2 ------- 我喜欢 Genius!谢谢 Jens。它有效。 - mainas
好的,我知道我还没有彻底研究,所以很明显我在这里走捷径 :-)。但是我只是想知道,使用了几种方法后,为什么有些方法返回List[Option]而其他方法返回List[Any]? - mainas
@MarkPeters 我同意。在那种情况下,使用异常处理是可以的。 - Jens Schauder
@mainas更新了我的答案,以解释您所看到的返回类型。 - Jens Schauder
显示剩余2条评论

1
另一种方式是:
data.toMap.apply('b')

或者通过一个中间步骤,这样更好:


val m = data.toMap
m('b')

这里隐式地使用了apply,即最后一行等价于:

m.apply('b')

@thetrystero:我认为这是由于toMap具有隐式参数所致。 - chris

1

有多种方法可以实现。另外一种方法:

scala> def listInt(ls:List[(Char, List[Int])],ch:Char) = ls filter (a => a._1 == ch) match {
 | case Nil => List[Int]()
 | case x ::xs => x._2
 | }
listInt: (ls: List[(Char, List[Int])], ch: Char)List[Int]
scala> listInt(data, 'b')
res66: List[Int] = List(1, 1)

0

当你确定它存在时,可以尝试添加类型信息。

val char = 'b'
data.collect{case (x,y:List[Int]) if x == char => y}.head

如果你不确定字符是否存在,可以使用headOption

data.collect{case (x,y:List[Int]) if x == char => y}.headOption

0

你也可以使用模式匹配来解决这个问题。请记住,你需要使其递归。解决方案应该类似于这样:

def findTupleValue(tupleList: List[(Char, List[Int])], char: Char): List[Int] = tupleList match {
  case (k, list) :: _ if char == k => list
  case _ :: theRest => findTupleValue(theRest, char)
}

这段代码将递归遍历你的元组列表。检查头部元素是否符合你的条件(即你要查找的键),然后返回它,或继续处理列表的其余部分。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接