将scala代码推广到函数中

时间:2011-11-23 22:44:04

标签: function generics scala haskell types

所以我最近不小心写了一个关于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类型系统屈服于我的意愿并将其概括为一个函数,其中xscompare是输入(理想情况下,带有比较器的咖喱输入)第一)。虽然这肯定是由于我对Scala的一般不熟悉,但我也略微责怪Scala的复杂(虽然非常强大)类型系统。你可以做一些手持操作,并指导我如何将其转换为通用函数,其类型签名类似于Haskell等价物? (阅读:一般为。)如果它比myMaxBy(myCompare)(someList)更复杂,请同时演示用法。

2 个答案:

答案 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类进行隐式转换非常有用,例如IntRichInt

或者,如果您希望能够灵活地更改排序条件,则可以这样写:

  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 { … } }
}

虽然这在语法上稍微清晰一点,但它不允许你传入一个显式的备用排序(比如说例如字符串的字符串排序)。