CLOVER🍀

That was when it all began.

Scalaz StringW

こちらは、意外とすぐにできたので。StringWです。

charsNel(e: ⇒ NonEmptyList[Char])

元のStringが空文字であれば、引数で渡された関数の返却値であるNonEmptyListが返ります。そうでなければ、元のStringをNonEmptyListに変換したものが返ります。

  println("hello charsNel => " + "hello".charsNel("default".toList.toNel.get))
  println("[] charsNel => " + "".charsNel("default".toList.toNel.get))

実行結果。

hello charsNel => NonEmptyList(h, e, l, l, o)
[] charsNel => NonEmptyList(d, e, f, a, u, l, t)
charsNel

引数無し版です。元のStringが空文字であればNoneが、そうでなければSomeに包まれたNonEmptyListが返ります。

  println("hello charsNel => " + "hello".charsNel)
  println("[] charsNel => " + "".charsNel)

実行結果。

hello charsNel => Some(NonEmptyList(h, e, l, l, o))
[] charsNel => None
charsNelErr

元のStringが空文字であれば、RuntimeExceptionがスローされ引数の文字列が例外メッセージになります。そうでければ、NotEmptyListにStringが変換されます。

  println("hello charsNelErr => " + "hello".charsNelErr("Not Empty"))
  println("hello charsNelErr => " + {
    try {
      "".charsNelErr("Empty")
    } catch {
      case e: RuntimeException => e.getMessage
    }
  })

実行結果。

hello charsNelErr => NonEmptyList(h, e, l, l, o)
hello charsNelErr => Empty
encode

暗黙のパラメータが必要です。StringをArray[Byte]に変換します。

  implicit val c = charset("UTF-8")
  println("日本語 encode => " + new String("日本語".encode, "UTF-8"))
  println("[] encode => " + new String("".encode, "UTF-8"))

なお、charset関数はScalaz.CharSetを返す関数です。

実行結果。

日本語 encode => 日本語
[] encode => 
fileEach

元のStringをファイル名として使い、ファイルを開いて1バイトずつ読み込みます。引数は、Byteを受けるコールバック関数。

  import scala.collection.mutable.ArrayBuffer
  val buffer = new ArrayBuffer[Byte]
  "src/main/scala/StringExample.scala".fileEach(b => buffer += b)
  println(new String(buffer.toArray, "UTF-8")) // このソースが出力される

なお、ファイルが存在しなかった場合は、java.io.FileNotFoundExceptionがスローされます。ソースを見た限り、これ系のメソッドはバッファリングされていないっぽいので微妙…。

実行結果。

/* ファイルの内容が出力されるサンプルのため、割愛 */
node(prefix: String, a: Option)

StringからXMLノードを作成します。元のStringはタグ名になり、第2引数にSomeを渡すと値として使用されます。

  println("tag node => " + {
    "tag".node("prefix", some("value"))
  } + ", " + {
    "tag".node("prefix", none[String])
  })
  println("[] node => " + {
    "".node("prefix", some("value"))
  } + ", " + {
    "".node("prefix", none[String])
  })

実行結果。

tag node => <prefix:tag>value</prefix:tag>, 
[] node => <prefix:>value</prefix:>, 

特に元の文字が空文字でも、エラーになったりせずタグ名が空になります。また、第2引数のOptionをNoneにすると、結果自体がなくなります。

node(prefix: String, attributes: MetaData, scope: NamespaceBinding, a: Option)

属性、名前空間が指定できるnodeメソッドっぽいのですが…。

  println("tag node => " + {
    "tag".node("prefix", null, null, some("value"))
  })
  println("[] node => " + {
    "".node("prefix", null, null, some("value"))
  })

実行結果。

tag node => <prefix:tag>value</prefix:tag>
[] node => <prefix:>value</prefix:>

ソースを見る限り、引数を使っているかどうか微妙…。
StringW.scala

  /**
   * Construct an XML node based on the given option value. If there is no value available, then an empty text node is returned,
   * otherwise, the string representation (using show) of the value is returned in an element with the given label.
   */
  def node[A: Show](prefix: String, attributes: MetaData, scope: NamespaceBinding, a: Option[A]): Node =
    a match {
      case Some(t) => Elem(prefix, s, Null, TopScope, t.text)
      case None => Text("")
    }
parseBoolean

元のStringがBooleanとして解析しようと試みます。結果はValidationとして返されます。

  "true".parseBoolean.foreach {
    bool => printf("Boolean parsed[%b]\n", bool)
  }
  "hoge".parseBoolean.foreach {
    bool => printf("Boolean parsed[%b]\n", bool)
  }

実行結果。

Boolean parsed[true]

例えば、Validation#foreachを使用すると、解析に失敗した場合は引数に渡した関数は実行されせん。

parseByte

parseBooleanと同様。

  "10".parseByte.foreach {
    b => printf("Byte parse[%d]\n", b)
  }
  "ten".parseByte.foreach {
    b => printf("Byte parse[%d]\n", b)
  }

実行結果。

Byte parse[10]
parseDouble, parseFloat, parseInt, parseLong, parseShort

割愛します。しかし、なぜかparseCharはありません

plural

複数形に変更した文字列を返します。引数は、数を表し、1つならそのまま、2以上なら複数形になります。

  println("book plural => " + "book".plural(1))
  println("book plural => " + "book".plural(2))
  println("[] plural => " + "".plural(2))

実行結果。

book plural => book
book plural => books
[] plural => s

とはいえ、辞書とかを内部に持っているわけではないので、複雑なルールなどには対応してません。

readFile

初期値と累積値および1バイトを受け取る引数を取り、ファイルを読み込みます。要は、ファイル版foldLeftです。

println("src/main/scala/StringExample.scala".readFile("", (s: String, b: Byte) => s + b.toChar)) // もちろん、これだと日本語は化けます

実行結果。

/* ファイルの内容が出力されるサンプルのため、割愛 */
readLines

1行辺りの初期値、行ごとの累積値と1文字、ファイル全体の初期値、行ごとの累積値と1行を引数にファイルを行単位に読み込みます。戻り値はUnitなので、結果は返せません。ここでは、行ヘッダとして「readLines:」を付けています。

  "src/main/scala/StringExample.scala".readLines("readLines:",
                                                 (xacc: String, c: Char) => { xacc + c },
                                                 "",
                                                 (yacc: String, line: String) => {
                                                   println(line)
                                                   yacc
                                                 })

実行結果。

/* 割愛 */
readLines:  // readLines
readLines:  "src/main/scala/StringExample.scala".readLines("readLines:",
readLines:                                                 (xacc: String, c: Char) => { xacc + c },
readLines:                                                 "",
readLines:                                                 (yacc: String, line: String) => {
readLines:                                                   println(line)
readLines:                                                   yacc
readLines:                                                 })
/* 割愛 */

ちなみに、CRとLFは意識していますがCRLFは意識していないっぽいので、CRLFのファイルに対してBufferedReader#readLine的な感覚で使うと、LFを受けた時に空文字を受け取ることになります。

s

元の文字列が取れます。

  println("hello.s => " + "hello".s)
  println("[].s => " + "".s)

実行結果。

hello.s => hello
[].s => 
unsafeCharsNel

元のStringを直接NonEmptyListに変換します。元のStringが空文字だった場合は、RuntimeExceptionがスローされます。

  println("hello unsafeCharsNel => " + "hello".unsafeCharsNel)
  println("[] unsafeCharsNel => " + {
    try {
      "".unsafeCharsNel
    } catch {
      case e: RuntimeException => e.getMessage
    }
  })

実行結果。

hello unsafeCharsNel => NonEmptyList(h, e, l, l, o)
[] unsafeCharsNel => cannot turn empty string into NonEmptyList

要は、例外メッセージが決まっているcharsNelErrメソッドです。実際の実相もそうなっています。

|:|

XMLノードを作成します。prefix指定無しのnodeメソッドと同義です。

  println("hello |:| => " + "hello".|:|(some("value")))
  println("[] |:| => " + "".|:|(some("value")))
hello |:| => <hello>value</hello>
[] |:| => <>value</>

XML系の機能と、ファイルの読み込み機能はちょっと怪しい感じですね。特にファイル読み込み機能はかなり微妙です。改行コードの扱いが変とか、バッファリングしてないとか、CharSetの指定ができないとか…。NonEmptyListへの変換とparse系のメソッドを使う感じかなぁ。

これで一応、目標にしていたBooleanW、IntW、ListW、OptionW、StringWは一通り見て回りました。