もう一回scalaでtreeコマンド

懲りずにまたやってみるw


ていうかあさいさんの書いたやつちょっといじるだけみたいなw

まったく改良にはなっていない、というか個人的になんとなくいじってみたくなっただけです、すいません(´Д`)
"こういう書き方もできるよ"的なサンプルとして、誰かの役にたてば・・・

import java.io.{File => jFile}

//パターンマッチの為だけに、FileとDirの objectとunapply定義したみたりとか

object Dir{
  def unapply( f:jFile ) = 
    if( f.isFile ) Some( f )
    else None
}

object File{
  def unapply( f:jFile ) = 
    if( f.isDirectory ) Some( f )
    else None
}

abstract class Node(file : jFile, indent: String ) {
  
  val branch : String
  val childBranch : String
  
  // メソッドの途中にprintlnとかあるの嫌なのでとりあえずtoStringにして純粋に
  // これだと、毎回計算するので、toStringと別のもので、lazy valとかにしてメモ化するのもありか?

  override def toString = { 
    indent + branch + file.getName + "\n" + {
      if (file.isDirectory)
        ls.foldLeft("")( _ + _.toString )
      else
        ""
    }
  }
// zipWithIndexで新しく生成しても、配列の最後かどうかの判断しか使ってないなら、これでもいい気が・・・
// この場合の問題は、Arrayのlastのオーダーが、 O(1) と O(n) のどっちなのか?(たぶん定数時間な気がするけど)
// あとで調べる or だれか教えて。 Arrayのlastのオーダー
// ていうか、l.last は、collectの外側で変数に束縛したほうが効率いいのか?
// まぁ効率だけ考えたら、もっと良いやり方ありそうだけど

  private def ls : Array[Node] = {
    try {
      val l = file.listFiles 
      l.collect { 
        case File(f) => {
          if( l.last eq f ) new LastDir(f, indent + childBranch)
          else new NormalDir(f, indent + childBranch)
        }
        case Dir(f) => {
          if( l.last eq f ) new LastFile(f, indent + childBranch)
          else new NormalFile(f, indent + childBranch)
        }
      }
    } catch {
      case e: NullPointerException => println("Permission Denied. [%s]".format(file))
      Array()
    }
  }
}

class RootDir(file : jFile) extends Node(file, "") { // 親コンストラクタに直接渡しちゃだめなの?
  val branch = ""
  val childBranch = ""
}

// なんだか、DRYじゃないので、ためしにtraitでつくってみるものの、 
// 微妙・・・てか行数増えてるw 改悪www
// classが5種類あるけど、classは減らして、代わりに、childBranchとかbranchもコンストラクタの引数で渡すっていうのもありかなぁ?

trait F{
  val childBranch = ""
}

trait Last{
  val branch = "`-- "
}

trait Normal{
  val branch = "|-- "
}

class NormalDir(file : jFile, indent : String) extends Node(file, indent) with Normal {
  val childBranch = "|   "
}

class NormalFile(file : jFile, indent : String) extends Node(file, indent) with F with Normal

class LastDir(file : jFile, indent : String) extends Node(file, indent) with Last {
  val childBranch = "    "
}

class LastFile(file : jFile, indent : String) extends Node(file, indent) with F with Last

これ書いてて思ったのが、matchの中で、

(f, indent + childBranch)

という、(生成するクラスは違うが)すべて同じ引数を渡していること。
これが、scala3.0で導入されるらしい、
Tupleと複数引数の互換性*1があれば、
一度、

val a = (f, indent + childBranch)

みたいに、束縛して、それを

  val a = (f, indent + childBranch)

  if( l.last eq f ) new LastDir(a)
  else new NormalDir(a)

っていうように渡せて、読みやすくて便利だね(・∀・)
っていうことですかね?

*1:こんな言い方でいいの?