私は学習目的のためにこの非常に小さな要件を持っています.
次の文字列があるとします
「1.1 これはテストです 34」
ここで、
「1.1」は章です
「これはテストです」は章のタイトルです
「34」はページ番号です
全体的な結果は、「解析された」行が問題ないかどうかを示すはずです。
現在、「整形式」の行でのみ機能しています(これは意図的なものです)。
これまでのところ、これを解決するには2つの方法があります...
1)モナドアプローチ(ただし、これが正しく行われるかどうかは完全にはわかりません。したがって、私の質問です)
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 のものを作成する高階関数を使用しようとすることを考えることができますが、今は物事をシンプルに保ちたいと思っています。私にとって重要なことはモナドのものです。
最後に、これが主なロジックです。
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 アプローチはやり過ぎに思えるかもしれませんが、これは学習目的であることを忘れないでください。
どちらのアプローチでも、論理フローは簡単に推論できることがわかりました。
どちらの場合も必要に応じて、文字列行を解析するためにステップを簡単に追加または削除できます。
このコードは非常に似ており、改善の余地があることはわかっていますが、現在はシンプルに保ち、将来的には共通のものを除外する予定です。
提案は大歓迎です。