我试图从Haskell移植这个特定的插入排序。在大多数情况下,如果List长于输入或有时复制的值,我会得到奇怪的错误输出。你看到我失踪的东西吗?或者我可能没有正确地从Haskell复制语法:
如果你提供修复,你可以使用类似的语义,我试图理解这个特定的版本。
object InsertionSortApp {
/*
* Based on Haskell version:
insert e [] = [e]
insert e lst@(x:xs)
| e < x = e : lst
| otherwise = x : (insert e xs)
insertionSort lst = insertionSort' lst [] where
insertionSort' [] lst = lst
insertionSort' (x:xs) lst = insertionSort' xs (insert x lst)
*/
def insert(e : Integer, lst : List[Int]) : List[Int] = {
def insertPrime(xs: List[Int]) : List[Int] = xs match {
case Nil => List(e)
case x :: xs if (e < x) => e :: lst
case x :: xs => x :: insertPrime(xs)
}
return insertPrime(lst)
}
def insertionSort(origList: List[Int]) : List[Int] = {
def insertionSortPrime(xs: List[Int], lst: List[Int]) : List[Int] = xs match {
case Nil => lst
case x :: xs => insertionSortPrime(xs, insert(x, lst))
}
insertionSortPrime(origList, List())
}
def main(args : Array[String]) : Unit = {
println("Running - Insertion Sort Test")
val lst = List(1, 7, 3, 4, 5)
println("Test: " + (insertionSort(lst)))
}
} // End of object //
答案 0 :(得分:5)
在insertPrime
中,更改此行
case x :: xs if (e < x) => e :: lst
到
case x :: xs if (e < x) => e :: x :: xs
答案 1 :(得分:2)
对于任何值得的东西,虽然Scala没有多次调度,但模式匹配有点接近(减去它是严格的):
def insert: (Int, List[Int]) => List[Int] = {
case (e, List()) => List(e)
case (e, lst @ (x :: xs)) =>
if (e < x) e :: lst
else x :: insert(e, xs)
}
def insertionSort(lst: List[Int]) = {
def `insertionSort'`: (List[Int], List[Int]) => List[Int] = {
case (List(), lst) => lst
case (x :: xs, lst) => `insertionSort'`(xs, insert(x, lst))
}
`insertionSort'`(lst, Nil)
}
我写了insert
和insertionSort'
作为返回函数,以避免明确命名参数,只是为了使Haskell的等价更清晰。当然,在普通的Scala代码中,我会收到一些参数并匹配它们。
答案 2 :(得分:0)
Haskell版本(以及您的Scala版本)可以简化。考虑:
insertSort xs = foldr insert [] xs
因此,Scala中的insertSort
方法归结为foldRight
来电。但是,由于Scala是一种严格的语言,因此应该首选foldLeft
。在Haskell中你可以写:
insertSort xs = foldl (flip insert) [] xs
所以你要做的就是在insert
中翻转参数顺序并在foldLeft
方法中调用insertSort
。
答案 3 :(得分:0)
尽管在编写Scala时,我过去喜欢函数式编程风格(通过组合器或递归)而不是命令式风格(通过变量和迭代),这个时间,针对这个特定问题,旧学校命令式嵌套循环从而产生更简单,更高效的代码。
我不认为回退到命令式样式是某些类问题的错误,例如排序算法通常会转换输入缓冲区(更像是一个过程),而不是产生新的排序集合。 / p>
这是我的解决方案:
package bitspoke.algo
import scala.math.Ordered
import scala.collection.mutable.Buffer
abstract class Sorter[T <% Ordered[T]] {
// algorithm provided by subclasses
def sort(buffer : Buffer[T]) : Unit
// check if the buffer is sorted
def sorted(buffer : Buffer[T]) = buffer.isEmpty || buffer.view.zip(buffer.tail).forall { t => t._2 > t._1 }
// swap elements in buffer
def swap(buffer : Buffer[T], i:Int, j:Int) {
val temp = buffer(i)
buffer(i) = buffer(j)
buffer(j) = temp
}
}
class InsertionSorter[T <% Ordered[T]] extends Sorter[T] {
def sort(buffer : Buffer[T]) : Unit = {
for {
i <- 1 until buffer.length
j <- i until 0 by -1
if (buffer(j) < buffer(j - 1))
}
swap(buffer, j, j - 1)
}
}
如您所见,为了实现参数多态,而不是使用java.lang.Comparable
,我更喜欢scala.math.Ordered
和Scala View Bounds而不是Upper Bounds。由于Scala Implicit Conversions of primitive types to Rich Wrappers,这当然有效。
您可以按如下方式编写客户端程序:
import bitspoke.algo._
import scala.collection.mutable._
val sorter = new InsertionSorter[Int]
val buffer = ArrayBuffer(3, 0, 4, 2, 1)
sorter.sort(buffer)
assert(sorter.sorted(buffer))