也许这可能很容易修复,但你可以帮助我或指导我找到解决方案。我有一个删除函数,它通过一个元组列表“List [(String,Any)]”,我试图在循环列表时用Nil替换值的1索引。
但是当我尝试用Nil替换当前的v时,它说v被分配给“val”。现在我明白scala列表是不可变的。也许这就是出了什么问题?
我尝试了Tail递归实现,但是当我退出def时,存在类型不匹配。即:是单位但需要:选项[任意]
// remove(k) removes one value v associated with key k
// from the dictionary, if any, and returns it as Some(v).
// It returns None if k is associated to no value.
def remove(key:String):Option[Any] = {
for((k,v) <- d){
if(k == key){
var temp:Option[Any] = Some(v)
v = Nil
return temp
}
}; None
}
这是试图找出
的另一种方式 def remove(key:String):Option[Any] = {
def removeHelper(l:List[(String,Any)]):List[(String,Any)] =
l match {
case Nil => Nil
case (k,v)::t => if (key == k) t else (k,v)::removeHelper(t)
}
d = removeHelper(d)
}
有什么建议吗?这是一个家庭作业/学校项目的想法我可能会为那些不喜欢帮助做作业的人添加这个。
答案 0 :(得分:2)
嗯,有很多方法可以回答这个问题。我将用我自己的实现概述我能想到的那些,但列表并非详尽无遗(也许,最佳实现)。
首先,您可以尝试使用现有的组合器 - 通常的嫌疑人是map
,flatMap
,foldLeft
和foldRight
:
def remove_flatMap(key: String, list: List[(String, Any)]): List[(String, Any)] =
// The Java developer in me rebels against creating that many "useless" instances.
list.flatMap {a => if(a._1 == key) Nil else List(a)}
def remove_foldLeft(key: String, list: List[(String, Any)]): List[(String, Any)] =
list.foldLeft(List[(String, Any)]()) {(acc, a) =>
if(a._1 == key) acc
else a :: acc
// Note the call to reverse here.
}.reverse
// This is more obviously correct than the foldLeft version, but is not tail-recursive.
def remove_foldRight(key: String, list: List[(String, Any)]): List[(String, Any)] =
list.foldRight(List[(String, Any)]()) {(a, acc) =>
if(a._1 == key) acc
else a :: acc
}
这些问题是,据我所知,一旦达到某个条件,你就无法阻止它们:我不认为它们会直接解决你的问题,因为它们会删除 all key
的实例,而不是第一个。
您还需要注意:
foldLeft
必须在列表完成后将其反转,因为它会以“错误”顺序附加元素。foldRight
没有那个缺陷,但不是尾递归:它会导致大型列表中的内存问题。map
不能用于您的问题,因为它只允许我们修改列表的值而不是其结构。您也可以使用自己的实现。我已经包含了两个版本,一个是尾递归,另一个不是。尾递归显然是更好的,但也更冗长(我责怪使用List[(String, Any)]
而不是Map[String, Any]
的丑陋:
def remove_nonTailRec(key: String, list: List[(String, Any)]): List[(String, Any)] = list match {
case h :: t if h._1 == key => t
// This line is the reason our function is not tail-recursive.
case h :: t => h :: remove_nonTailRec(key, t)
case Nil => Nil
}
def remove_tailRec(key: String, list: List[(String, Any)]): List[(String, Any)] = {
@scala.annotation.tailrec
def run(list: List[(String, Any)], acc: List[(String, Any)]): List[(String, Any)] = list match {
// We've been aggregating in the "wrong" order again...
case h :: t if h._1 == key => acc.reverse ::: t
case h :: t => run(t, h :: acc)
case Nil => acc.reverse
}
run(list, Nil)
}
更好的解决方案当然是使用正确的工具:Map[String, Any]
。
请注意,我认为我不完全回答您的问题:我的示例删除 key
,而您希望将其设置为Nil
。由于这是您的作业,我会让您弄清楚如何更改我的代码以符合您的要求。
答案 1 :(得分:0)
List
是错误的集合。您应该使用Map[String,Any]
。有了清单,
我想第2点可能就是为什么你要用Nil
替换它而不是从列表中删除密钥。 Nil
在这里使用是不对的,真的。如果您尝试检索不存在的密钥而不是已删除的密钥,那么您将获得不同的回报。这真的是你想要的吗?返回Some(Nil)
会有多大意义?
这里有几种方法可以使用可变列表或不可变列表,但是并不假设你成功地停止了重复爬行......
val l1: List[(String, Any)] = List(("apple", 1), ("pear", "violin"), ("banana", Unit))
val l2: List[(Int, Any)] = List((3, 1), (4, "violin"), (7, Unit))
def remove[A,B](key: A, xs: List[(A,B)]) = (
xs collect { case x if x._1 == key => x._2 },
xs map { case x if x._1 != key => x; case _ => (key, Nil) }
)
scala> remove("apple", l1)
res0: (List[(String, Any)], List[(String, Any)]) = (List((1)),List((apple, List()),(pear,violin), (banana,object scala.Unit)))
scala> remove(4, l2)
res1: (List[(Int, Any)], List[(Int, Any)]) = (List((violin)),List((3,1), (4, List()), (7,object scala.Unit)))
scala> remove("snark", l1)
res2: (List[Any], List[(String, Any)]) = (List(),List((apple,1), (pear,violin), (banana,object scala.Unit)))
返回一个匹配值列表(如果没有匹配则为空列表而不是None
)和元组中的剩余列表。如果您想要一个完全删除不需要的密钥的版本,请执行此操作...
def remove[A,B](key: A, xs: List[(A,B)]) = (
xs collect { case x if x._1 == key => x._2 },
xs filter { _._1 != key }
)
但也看看这个:
scala> l1 groupBy {
case (k, _) if k == "apple" => "removed",
case _ => "kept"
}
res3: scala.collection.immutable.Map[String,List[(String, Any)]] = Map(removed -> List((apple,1)), kept -> List((pear,violin), (banana,object scala.Unit)))
这是你可以开发的东西。您需要做的就是将("apple", Nil)
添加到“保留”列表中,并从“已删除”列表中提取值。
请注意,我使用List组合函数而不是编写自己的递归代码;这通常会使代码更清晰,并且通常比手动递归函数更快或更快。
另请注意,我不会更改原始列表。这意味着我的函数适用于可变列表和不可变列表。如果您有一个可变列表,请随意将我返回的列表指定为您的mutable var
的新值。赢,赢。
但请使用地图。看看事情变得多么简单:
val m1: Map[String, Any] = Map(("apple", 1), ("pear", "violin"), ("banana", Unit))
val m2: Map[Int, Any] = Map((3, 1), (4, "violin"), (7, Unit))
def remove[A,B](key: A, m: Map[A,B]) = (m.get(key), m - key)
scala> remove("apple", m1)
res0: (Option[Any], scala.collection.immutable.Map[String,Any]) = (Some(1),Map(pear -> violin, banana -> object scala.Unit))
scala> remove(4, m2)
res1: (Option[Any], scala.collection.immutable.Map[Int,Any]) = (Some(violin),Map(3 -> 1, 7 -> object scala.Unit))
scala> remove("snark", m1)
res2: res26: (Option[Any], scala.collection.immutable.Map[String,Any]) = (None,Map(apple -> 1, pear -> violin, banana -> object scala.Unit))
组合器功能使事情变得更容易,但是当你使用正确的集合时,它变得如此简单,以至于几乎不值得编写特殊功能。当然,除非您试图隐藏数据结构 - 在这种情况下,您应该将其隐藏在对象中。