下面是一个函数的实现,它返回字典上的下一个排列。这在Euler问题中很有用。
它是写在Strings上的(我需要它)。但是,它应该适用于任何可比较值的索引序列。我已经尝试通过将String的两次出现更改为IndexedSeq [Char]来推广它,但这会出错:
euler-lib.scala:26: error: type mismatch;
found : IndexedSeq[Char]
required: String
((n.slice(pivot+1, successor):+ n(pivot)) + n.drop(successor+1)).reverse
^
为什么类型推断器会在那里推断出String?我似乎没有做任何需要字符串的操作?
我可以通过使用IndexedSeq [“可比较的东西”]使其更加通用吗?我无法做到这一点。
// return the lexographically next permutation to the one passed as a parameter
// pseudo-code from an article on StackOverflow
def nextPermutation(n:String):String = {
// 1. scan the array from right-to-left
//1.1. if the current element is less than its right-hand neighbor,
// call the current element the pivot,
// and stop scanning
// (We scan left-to-right and return the last such).
val pivot = n.zip(n.tail).lastIndexWhere{ case (first, second) => first < second }
//1.2. if the left end is reached without finding a pivot,
// reverse the array and return
// (the permutation was the lexicographically last, so its time to start over)
if (pivot < 0) return n.reverse
//2. scan the array from right-to-left again,
// to find the rightmost element larger than the pivot
// (call that one the successor)
val successor = n.lastIndexWhere{_ > n(pivot)}
//3. swap the pivot and the successor, and
//4. reverse the portion of the array to the right of where the pivot was found
return (n.take(pivot) :+ n(successor)) +
((n.slice(pivot+1, successor):+ n(pivot)) + n.drop(successor+1)).reverse
}
答案 0 :(得分:4)
+
中的方法IndexedSeq
用于生成包含一个额外给定元素的新序列,但您希望生成一个包含其他序列的元素。方法是++
,因此您的最后一行必须如下所示:
(n.take(pivot) :+ n(successor)) ++
((n.slice(pivot+1, successor):+ n(pivot)) ++ n.drop(successor+1)).reverse
您看到这个奇怪的编译器消息,因为String
的签名不匹配,因此需要+
,因此用于字符串连接的显式转换会启动(此转换是因为它让你写的是List(8) + " Test"
)。
编辑:对有序元素的序列类型进行推广:
正如我在评论中所说,序列的泛化有点复杂。除了元素类型A
之外,您还需要另一种表示序列的CC[X] <: SeqLike[X,CC[X]]
类型。通常C <: SeqLike[A,C]
就足够了,但类型推断器不喜欢那个(在调用该方法时,您总是需要传递A
和C
的类型。)
如果您只是以这种方式更改签名,编译器会抱怨它需要隐式CanBuildFrom[CC[A],A,CC[A]]
参数,因为需要参数,例如通过reverse
方法。该参数用于从另一个序列类型构建一个序列类型 - 只需搜索站点以查看集合API如何使用它的一些示例。
最终结果如下:
import collection.SeqLike
import collection.generic.CanBuildFrom
def nextPermutation[A, CC[X] <: SeqLike[X,CC[X]]](n: CC[A])(
implicit ord: Ordering[A], bf: CanBuildFrom[CC[A],A,CC[A]]): CC[A] = {
import ord._
// call toSeq to avoid having to require an implicit CanBuildFrom for (A,A)
val pivot = n.toSeq.zip(n.tail.toSeq).lastIndexWhere{
case (first, second) => first < second
}
if (pivot < 0) {
n.reverse
}
else {
val successor = n.lastIndexWhere{_ > n(pivot)}
(n.take(pivot) :+ n(successor)) ++
((n.slice(pivot+1, successor):+ n(pivot)) ++ n.drop(successor+1)).reverse
}
}
这样,如果您将方法传递给方法,则会得到Vector[Int]
,如果将方法传递给List[Double]
,则会得到String
。那么Seq[Char]
呢?这些不是实际的序列,但可以隐式转换为Seq[A]
。有可能改变该方法的定义,期望某些类型可以隐式转换为String
,但是再次类型推断不能可靠地工作 - 或者至少我无法使其可靠地工作。作为一种简单的解决方法,您可以为def nextPermutation(s: String): String =
nextPermutation[Char,Seq](s.toSeq).mkString
s定义另一种方法:
{{1}}
答案 1 :(得分:1)
小提示:
n(pivot)) + n.drop(successor+1)
^
如果出现类型不匹配错误,并且^
指向最后一个参数列表的第一个括号(即,它将指向(
中的第二个x.foldLeft(y)(z)
),这意味着该方法返回的值的类型错误。
或者,在这种情况下,n.drop(sucessor+1)
的类型为IndexedSeq[Char]
,但+
方法需要String
。
另一个小提示:接受+
的唯一内容是数字类和String
。如果您尝试添加内容并收到错误,则很可能是Scala认为您使用+
添加Strings
。例如:
true + true // expected String, got Boolean error
"true" + true // works, the second true is converted to String
true + "true" // works, the first true is converted to String
因此,除非使用数字或字符串,否则请避免使用+
。
所以,关于制作那个将军......
def nextPermutation[A <% Ordered[A]](n: IndexedSeq[A]): IndexedSeq[A] = {
val pivot = n.zip(n.tail).lastIndexWhere{ case (first, second) => first < second }
if (pivot < 0) return n.reverse
val successor = n.lastIndexWhere{_ > n(pivot)}
return (n.take(pivot) :+ n(successor)) ++
((n.slice(pivot+1, successor):+ n(pivot)) ++ n.drop(successor+1)).reverse
}
简单的部分就是声明IndexedSeq
。但是你必须在A
上进行参数化,并且必须有一种订购A
的方法,以便您可以比较元素(<%
表示从A
到Ordered[A]
的隐式转换def nextPermutation[A : Ordering](n: IndexedSeq[A]): IndexedSeq[A] = {
val ordering = implicitly[Ordering[A]]; import ordering._
val pivot = n.zip(n.tail).lastIndexWhere{ case (first, second) => first < second }
if (pivot < 0) return n.reverse
val successor = n.lastIndexWhere{_ > n(pivot)}
return (n.take(pivot) :+ n(successor)) ++
((n.slice(pivot+1, successor):+ n(pivot)) ++ n.drop(successor+1)).reverse
}
可用)。声明它的另一种方式是这样的:
A : Ordering
此处,Ordering[A]
表示存在可用的隐式<
,然后将其获取并导入范围,以便它可以提供隐式转换以使Ordered[A]
起作用。 Ordering[A]
和{{1}}之间的差异可以在其他问题上找到。
答案 2 :(得分:0)
在Scala 2.8.0中,代码正确编译。您使用的是哪个版本的Scala?
scala> nextPermutation("12354")
res0: String = 12435
答案 3 :(得分:0)
问题24让我难倒了一段时间:
println("0123456789".permutations.drop(1000000 - 1).next);