scala中的函数链接

时间:2014-09-23 02:15:18

标签: scala method-chaining

我似乎无法弄清楚如何将这些功能链接在一起,任何帮助或建议都将受到赞赏。

// Generic approach to adding flags to a command string
trait UpdateCommandString {
  def update[T](option: Option[T], flagName: String)(implicit command: String): String = {
    if (option.isEmpty)
      command
    else if (option.get.isInstanceOf[Boolean]) {
      if (option.get.asInstanceOf[Boolean])
        s"$command $flagName"
      command
    } else
      s"$command $flagName ${option.get.asInstanceOf[String]}"        
  }
}

// One example of flags (the program I'm using has literally 50+ flags
// so there will be a number of case classes that group them into related
// sets)
case class Flags(cache: Option[String] = None,
  errorlog: Option[String] = None,
  accesslog: Option[String] = None,
  verbose: Option[Boolean] = Some(false),
  auth: Option[Boolean] = Some(false)) extends UpdateCommandString {

  def applyToCommand(implicit command: String): String = {
    // These seem to apply separately, but I want to chain 
    // them together!
    update(cache, "-cache")
    update(errorlog, "-error")
    update(accesslog, "-access")
    update(auth, "-do-auth")
  }
}

// An example of what I'm trying to do
// Given a base command string and a bunch of case classes to apply
// to that string, I'd like to be able to call applyToCommand and 
// get back the modified command string
var command = "run_system"
val f = Flags(Some("asdfasdf"), None, None, Some(true), Some(false))
command = f.applyToCommand(command)

2 个答案:

答案 0 :(得分:4)

我建议您完全重新设计当前的方法。

Flags课程的每个成员都应该是自己的案例类,扩展常见的Flag类。

因此,您可以定义函数以将不同的标志组合到一个配置中。在最后一步中,此配置可用于构建结果字符串。

abstract class Flag(name: String, parameter : Option[String])
case class Cache(parameter : Option[String]) extends Flag("-cache", parameter)
case class ErrorLog(parameter : Option[String]) extends Flag("-errorlog", parameter)
//...

type Config = List[Flag]

def applyToCommand(commandName : String, config : Config) = {
  def buildString(f:Flag) = 
    s" $f.name${f.parameter.map(" " ++ _).getOrElse("")}"

  val flagsString = config.map(buildString).mkString("")
  s"$commandName" ++ flagString
}

//Now you can it simply use it as I described above
val config = List(Cache(Some("asdf")), ErrorLog(None))
applyToCommand("run_system", config)

这使您的代码更灵活,更容易重构。

最后,我们建议您如何修改此设计以更好地满足您的需求:

  • 如果需要对标志进行分组,可以将它们放在对象或单独的文件中。或者,如果您想根据组更改其行为,可以增强类层次结构并添加中间层。

  • 您可以将参数从Flag向下移动到案例类,这样每个Flag都可以定义它是否需要参数,如果是,是多少以及是否可选。

    < / LI>
  • 您还可以在案例类中实现buildString,以便每个标志可以决定如何自行格式化。

  • 如果你想添加新的Flags,你只需添加一个新的类就可以了,不需要在不相关的类中添加任何内容。

答案 1 :(得分:0)

正如@bmaderbacher所解释的那样,我认为你应该将不同的案例类中的不同标志分开。

但要回答您的问题,您应该修改applyToCommand

def applyToCommand(implicit command: String): String = {
   var s = update(cache, "-cache")(command)
   s = update(errorlog, "-error")(s)
   s = update(accesslog, "-access")(s)
   s = update(auth, "-do-auth")(s)
   s
}

此时应该很清楚,您没有为Flag课做出正确的选择。

我会做那样的事情:

trait Flag {
   def toString: String
}

case class Command(value: String) {
   def add(flag: Flag) = Command(value + ' ' + flag.toString)
   def +(flag: Flag) = add(flag) 
}

case class Cache(size: Int) extends Flag {
   def toString = s"--cache $size"
}

case object Auth extends Flag {
   def toString = "--auth"
}

现在您可以执行以下操作:

val command = Command("run") + Cache(500) + Auth