在Scala中的基本插入排序,Haskell版本的端口

时间:2011-05-03 20:32:41

标签: java sorting scala haskell

我试图从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 // 

4 个答案:

答案 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)
}

我写了insertinsertionSort'作为返回函数,以避免明确命名参数,只是为了使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))