所以我最近不小心写了一个关于Scala问题的Haskell答案。对Haskell比较熟悉,解决方案对我很轻松:
myMaxBy :: (a -> a -> Ordering) -> [a] -> [a]
myMaxBy _ [] = undefined
myMaxBy f (x:xs) = foldr step [x] xs
where step y acc@(z:_) = case f y z of
GT -> [y]
EQ -> y:acc
LT -> acc
然后有人提醒我这是一个Scala问题。我开始将我的代码转换为Scala,经过很多痛苦后我决定:
(List(xs.head) /: xs.tail) { (acc, y) =>
y compare acc.head match {
1 => List(y)
0 => y :: acc
-1 => acc
}
}
但我无法为我的生活让Scala类型系统屈服于我的意愿并将其概括为一个函数,其中xs
和compare
是输入(理想情况下,带有比较器的咖喱输入)第一)。虽然这肯定是由于我对Scala的一般不熟悉,但我也略微责怪Scala的复杂(虽然非常强大)类型系统。你可以做一些手持操作,并指导我如何将其转换为通用函数,其类型签名类似于Haskell等价物? (阅读:一般为。)如果它比myMaxBy(myCompare)(someList)
更复杂,请同时演示用法。
答案 0 :(得分:13)
您错过了模式匹配中的case
个关键字,这非常重要!
您需要的只是集合参数类型能够使用compare
方法。 Scala中有两个用于比较事物的系统:扩展Ordered
或使用Ordering
。两者之间存在隐含的转换,因此您选择的内容并不重要;第一个可能更容易理解。
首先,使用Ordered
:
def myMaxBy[A <% Ordered[A]](xs: List[A]) = {
(List(xs.head) /: xs.tail) { (acc, y) =>
y compare acc.head match {
case 1 => List(y)
case 0 => y :: acc
case -1 => acc
}
}
}
在这里,我们使用A
为通用类型<%
提供视图绑定,这意味着“可以被视为”。这比使用上限<:
更为通用,对于不是Ordered
的类,但对Ordered
类进行隐式转换非常有用,例如Int
至RichInt
。
或者,如果您希望能够灵活地更改排序条件,则可以这样写:
def myMaxBy[A](xs: List[A])(implicit ord: Ordering[A]) = {
(List(xs.head) /: xs.tail) { (acc, y) =>
ord.compare(y, acc.head) match {
case 1 => List(y)
case 0 => y :: acc
case -1 => acc
}
}
}
调用时,如果范围中存在隐式Ordering[A]
,则可以保留第二个参数。第二种方式还有一个优点,即您可以在任意类上定义Ordering
,无论它们是否已经支持它。
您可以使用例如myMaxBy(List(1,2,3,4,3,4))
来调用它们。在第二种情况下,如果您想要反向排序:myMaxBy(List(1,2,3,4,3,4))(Ordering.Int.reverse)
。
您可能在此上下文中看到的另一件事是上下文边界。例如。 [A: Ordering]
。这意味着与[A](implicit ord: Ordering[A])
相同,这更简洁,除了您没有得到Ordering
的句柄,因此必须使用implicitly
召唤它。因此,最好在上面明确说明它。
答案 1 :(得分:2)
您需要将A类型扩展为Ordered或更好,限制它以便有可用的Ordering类型。第一个看起来像:
def sort[A <: Ordered[A]](xs: List[A]) =
(List(xs.head) /: xs.tail) { (acc, y) => y compare acc.head match { … } }
由于Luigi指出,更好的是使用视图绑定&lt;%而不是子类型绑定&lt;:因为它允许对Ordered视图进行隐式转换的类型。
def sort[A <% Ordered[A]](xs: List[A]) =
(List(xs.head) /: xs.tail) { (acc, y) => y compare acc.head match { … } }
更好的是使用Ordering类型:
def sort[A](xs: List[A])(implicit ord: Ordering[A]) =
(List(xs.head) /: xs.tail) { (acc, y) => ord compare (y, acc.head) match { … } }
您也可以使用上下文绑定:
def sort[A: Ordering](xs: List[A]) = {
val ord = implicitly[Ordering[A]]
(List(xs.head) /: xs.tail) { (acc, y) => ord compare (y, acc.head) match { … } }
}
虽然这在语法上稍微清晰一点,但它不允许你传入一个显式的备用排序(比如说例如字符串的字符串排序)。