2

我对 Scala 很陌生,我遇到了一个问题。

我正在尝试编写一个包含函数队列的类。我希望能够将函数添加到队列中,并且在添加了所有函数后,运行这些函数。本质上构建一个表达式,例如:“function1(function2(function3()))”,然后返回然后进行评估。这是我到目前为止的代码:

  class Pipeline() {

    // Queue of functions to run
    private var queue: Queue[ _ => _] = new LinkedList();

    // Add functions to the queue 
    def addFunction(func:_ => _ ): Unit ={
      queue.add(func)
    }

    // Run all the functions in the queue
    def run(): Unit = {
      val function = runHelper(queue.poll(), queue)
      function
    }

    def runHelper(func: _ => _, queue: Queue[_ => _]): _  = {
      // Recursion base case
      if(queue.isEmpty)
        return func
      // Keep building the function recursively
      else
        func(runHelper(queue.poll(), queue))        
    }    
  }

我敢肯定,这里不止一个错误。但现在,我坚持的是 runHelper 函数的返回类型。如您所见,我正在尝试使用 _ 通配符,但这会产生编译错误。我将如何定义该函数将返回一个函数?我是否以一种好的方式来解决这个问题 - 如果不是,请指出我对问题的更合适解决方案的方向。

编辑1:澄清 函数的输入和返回类型事先不知道,它们的顺序需要可以动态分配。

Edit2:更多问题 我一直试图让 Edmondo1984 建议的代码按照我想要的方式工作,但我似乎无法得到它。

我需要做的是这样的:

val func1: String => File = function1
val func2: File => File = function2

var queue = func1

if(runFunc2)
    queue = queue :: func2

queue("exampleString")

我特别需要知道的是如何能够执行“queue = queue :: func2”。由于 :: 返回一个 FunctionQueue,我会想象我可以将它分配给 queue 变量。但话又说回来,我猜是因为变量的第一次初始化导致它具有“String => File”需求。我觉得我在这里有点过头了,任何帮助都将不胜感激。

4

5 回答 5

3

我更喜欢函数的形式是:(parameter list) => returnType.

这是我认为您的代码应该是什么样子的。我的 IDE 喜欢它,但这不能保证:

class Pipeline[T]() {

 // Queue of functions to run
 private var queue: util.Queue[ (T) => T] = new util.LinkedList()

 // Add functions to the queue
 def addFunction(func: (T)=> T ) {
   queue.add(func)
 }

 // Run all the functions in the queue
 def run() {
   val function = runHelper(queue.poll(), queue)
   function
 }

 def runHelper(func: (T) => T, queue: util.Queue[(T)=> T ]): (T)=>T = {
   // Recursion base case
   if(queue.isEmpty)
     func
   // Keep building the function recursively
   else
     func compose runHelper(queue.poll(), queue)
  }
}
于 2012-07-11T13:52:00.130 回答
3

你可以试试函数组合。比如像这样。

val f = (x:Int) => 2 * x
val g = f.compose(f)

如果输入和输出类型不同,您必须注意输入和输出匹配...

于 2012-07-11T13:53:37.887 回答
3

您尝试执行的操作是不可能的,因为 _ 是存在类型的占位符:存在但当前不相关的类型:例如,当您想要打印如下列表时可以使用它:

scala>  def aMethod(a:List[_]) = println(a.size)
aMethod: (a: List[_])Unit

scala>  val b = List(2,3,4)
b: List[Int] = List(2, 3, 4)

scala>  aMethod(b)
3

这是有效的,因为实际上您不会访问列表中的元素,因此您可以想象您不需要它们的类。

因为 Scala 是一种强类型语言,即编译器检查签名是否在您的代码中得到尊重。Queue[ _ => _] 是从未知类型到未知类型的函数队列,它没有用,因为如果您尝试弹出一个函数并将其应用于输入参数,您将无法验证该输入参数是否与签名匹配。

您尝试做的事情并非易事,您需要以递归方式定义队列。您可能想阅读 Shapeless HLIST implementation,但想法如下:

trait FunctionQueue[A,B]{

  def ::[C](newFunction: C => A): FunctionQueue[C,B]

  def apply(a:A):B
}

class FunctionQueueImpl[A,B,C](val f:A=>B, val queue:FunctionQueue[B,C]) extends FunctionQueue[A,C]{

  def apply(a:A) = queue.apply(f(a))

  def ::[D](newFunction: (D) => A):FunctionQueue[D,C] = new FunctionQueueImpl[D,A,C](newFunction,this)
}

object FunctionQueue {
  def EmptyQueue[T]:FunctionQueue[T,T] = new FunctionQueue[T,T] {
    def ::[C](newFunction: (C) => T):FunctionQueue[C,T] = new FunctionQueueImpl[C,T,T](newFunction,this)

    def apply(a: T):T = a
  }

  implicit def functionToQueue[A,B](function:A => B):FunctionQueue[A,B] = new FunctionQueueImpl(function, EmptyQueue[B])
}

现在你可以在 repl 中尝试一下:

scala>  import FunctionQueue._
import FunctionQueue._

scala>  val a: Int => Int = _ * 10
a: Int => Int = <function1>


scala>  val b: Double => Int = _.toInt 
b: Double => Int = <function1>

scala>  val c : String => Double = _.toDouble
c: String => Double = <function1>

scala>  val queue = c::b::a
queue: FunctionQueue[String,Int] = FunctionQueueImpl@cccfa5e

scala>  queue("1.25")
res1: Int = 10

scala>  queue("3.25")
res2: Int = 30

我建议阅读 Miles Sabins 的作品以了解更多信息。

于 2012-07-11T14:03:00.700 回答
1

关于实现,您可以使用一个简单List的函数T => T(称为 endofunctions)。

现在,在 scalaz 中,您有一个用于此类函数的Monoid(由Semigroupand构建)的实例。Zero这意味着,您可以编写以下代码:

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> val f = ((_:Int) + 6).endo
f: scalaz.Endo[Int] = <function1>

scala> val g = ((_:Int) * 4).endo
g: scalaz.Endo[Int] = <function1>

scala> val h = (-(_:Int)).endo
h: scalaz.Endo[Int] = <function1>

scala> f :: g :: h :: nil
res3: List[scalaz.Endo[Int]] = List(<function1>, <function1>, <function1>)

scala> .asMA.sum
res4: scalaz.Endo[Int] = <function1>

scala> f(g(h(1)))
res5: Int = 2

scala> res4(1)
res6: Int = 2
于 2012-07-11T15:02:51.753 回答
0

您可以像这样指定函数的类型:

Function[T, T]

或者

T => T

假设你的函数的输入和输出是相等的,它们确实应该是相等的。当然,您必须指定或替换T

如果你真的有可变输入-输出,整个方案会变得复杂得多。

于 2012-07-11T13:35:08.957 回答