0

出于学习目的,我有这个非常小的要求。

假设我们有以下字符串

“1.1 这是一个测试 34”
其中
“1.1”是章节
“这是一个测试”是章节的标题
“34”是页码

总体结果应该给我一些指示“解析”行是否可以。
现在它只适用于“格式良好”的行(这是故意的)。

到目前为止,我有 2 种方法来解决这个问题......
1)Monad 方法(虽然我不完全确定这是正确的,因此我的问题)

trait Mine[A] {
 def get(): A
 def map(f: A => A): Mine[A]
 def flatMap(f: A => Mine[A]): Mine[A]
}

case class Attempt1[A, B](a: (A, B)) extends Mine[(A, B)] {
 def get(): (A, B) = a
 def map(f: ((A, B)) => (A, B)): Mine[(A, B)] = {
   Attempt1(f(a._1, a._2))
}
 def flatMap(f: ((A, B)) => Mine[(A, B)]): Mine[(A, B)] = {
   f(a._1, a._2)
 }
}

而且我还有以下功能可以从我的“字符串”行中获取文本

def getChapter2(t: (Result, String)): Mine[(Result, String)] = {
 val result = t._1
 val state = t._2
 result.chapter = state.substring(0, 3)
 var newState = state.substring(3)
 Attempt1((result, newState))
}

def getTitle2(t: (Result, String)): Mine[(Result, String)] = {
 val result = t._1
 val state = t._2
 result.title = state.substring(0, state.length() - 2)
 var newState = state.substring(state.length() - 2)
 Attempt1((result, newState))
}

def getPage2(t: (Result, String)): Mine[(Result, String)] = {
 val result = t._1
 val state = t._2
 result.page = state
 Attempt1((result, ""))
}

我可以考虑尝试对从 Tuple2 中“取出”值并创建 Attempt1 东西的代码使用更高阶的函数,但现在我想让事情保持简单,对我来说重要的是 monad 的东西。

最后,这是主要逻辑。

var line = "1.1 Some awesome book 12"
val result = new Result("", "", "")    
val at1 = Attempt1((result, line))

val r = for (
  o1 <- at1;
  o2 <- getChapter2(o1);
  o3 <- getTitle2(o2);
  o4 <- getPage2(o3)
) yield (o4)

val res = r.get._1
println("chapter " + res.chapter) //1.1
println("title " + res.title) // Some awesome book
println("page " + res.page) // 12

2) 组合方法

def getChapter(t: (Result, String)): (Result, String) = {
 val result = t._1
 val state = t._2
 result.chapter = state.substring(0, 3)
 var newState = state.substring(3)
 (result, newState)
}

def getTitle(t: (Result, String)): (Result, String) = {
 val result = t._1
 val state = t._2
 result.title = state.substring(0, state.length() - 2)
 var newState = state.substring(state.length() - 2)
 (result, newState)
}

def getPage(t: (Result, String)): (Result, String) = {
 val result = t._1
 val state = t._2
 result.page = state
 (result, "")
}

正如你所看到的,除了返回类型(不是由 Mine 类型“包装”)之外,函数是相同的,而且我也有这个方法

def process(s: String, f: ((Result, String)) => (Result, String)): Result = {
 val res = new Result("", "", "")
 val t = f(res, s)
 res
}

我的主要逻辑如下

var line = "1.1 Some awesome book 12"
var fx = getChapter _ andThen getTitle _ andThen getPage
var resx = process(line, fx)
printf("title: %s%nchapter: %s%npage: %s%n", resx.title, resx.chapter, resx.page)

返回值与“Monad 方法”相同。

所以最后的问题是:
“Monad 方法”真的是 Monad 吗?
我发现组合方法逻辑更容易,对于这种特殊情况,Monad 方法可能看起来有点矫枉过正,但请记住这是出于学习目的。

我发现在这两种方法中,逻辑流程都很容易推理。

如果在这两种情况下都需要,很容易添加甚至删除一个步骤以解析字符串行。

我知道这段代码非常相似并且有改进的余地,但现在我保持简单,也许将来我会考虑到共同点。

欢迎提出建议。

4

1 回答 1

0

首先,您的代码中不需要vars 。其次,由于您使用substring的是字符串函数,因此您只需要一个获取子字符串偏移量的部分函数。这将是重构时的一个很好的起点,并且允许在格式更改时如何拆分行的各种功能。

这看起来像

def splitline(method:String)(symbol:String)(s:String) = method match {
  case "substring" => val symb = Integer.parseInt(symbol) ;(s.substring(0,symb),s.substring(symb))
}

val getTitle = splitline("substring")("3") _

就组成或一元代码而言,这取决于偏好和您希望对自己施加的认知负担。

于 2013-08-15T08:09:50.000 回答