Programming in Scala(Second Edition)読書ノート10

11794 ワード

  • コンソールで文字でグラフィックを印刷するプログラムを見たことがあるに違いありません.この章はグラフィック要素を定義することから始まります.私たちのグラフィック要素はすべていくつかの文字からなる矩形
  • です.
    abstract class Element {
      def contents: Array[String]
      def height: Int = contents.length
      def width: Int = if (height == 0) 0 else contents(0).length
    }

    上で定義した3つの方法にはパラメータがなく、括弧も省きました.このような方法を無参法(parameterless method)と呼ぶ.
    Such parameterless methods are quite common in Scala. By contrast, methods defined with empty parentheses, such as def height(): Int, are calledempty-paren methods. The recommended convention is to use a parameterless method whenever there are no parameters and the method accessesmutable state only by reading fields of the containing object (in particular, itdoes not change mutable state). This convention supports the uniform accessprinciple,1 which says that client code should not be affected by a decisionto implement an attribute as a field or method.規則:パラメータがない場合はカッコを省略し、オブジェクトのプロパティを読み込むだけです.
    この約束は、1つのプロパティをドメインとして実装してもメソッドとして実装しても、顧客コードが影響を受けるべきではないという「統合アクセスの原則」をサポートします.次の定義を見てみましょう
    abstract class Element {
      def contents: Array[String]
      val height = contents.length
      val width = if (height == 0) 0 else contents(0).length
    }

    メソッドをfieldに変更すると、クライアントコードは依然として同じ方法で要素の高さと幅にアクセスできます.これがいわゆる「統一訪問の原則」です
    2.fieldで無パラメトリックmethodを再ロードする
    The uniform access principle is just one aspect where Scala treats fields andmethods more uniformly than Java. Another difference is that in Scala, fieldsand methods belong to the same namespace. This makes it possible for afield to override a parameterless method. For instance, you could changethe implementation of contents in class ArrayElement from a method toa field without having to modify the abstract method definition of contentsin class Element
    class ArrayElement(conts: Array[String]) extends Element {
      val contents: Array[String] = conts
    }

    一方,メソッドとドメインは同じネーミング空間に属するため,名前が同じメソッドとドメインを定義することはできない.
    3.パラメトリックドメイン(parametric field)
    Consider again the definition of class ArrayElement shown in the previoussection. It has a parameter conts whose sole purpose is to be copied into thecontents field. 上記ArrayElementの定義では、パラメータcontsが存在する唯一の役割はドメインcontentsにコピーされることである.このような余分なコードを避けることができますか?parametric fieldでできます.ArrayElementの定義をparametric fieldで書き換えます
    class ArrayElement(
        val contents: Array[String]
    ) extends Element

    This is a shorthand that defines at the same time a parameter and field with the same name.
    4.親クラスのコンストラクション関数を呼び出す
    class LineElement(s: String) extends ArrayElement(Array(s)) {
      override def width = s.length()
      override def height = 1
    }

    LineElement needs to passan argument to the primary constructor of its superclass. To invoke a superclass constructor, you simply place the argument or arguments you want topass in parentheses following the name of the superclass
    渡すパラメータを親クラス名の後ろのカッコに入れるだけです
    5.いつ方法の前にoverrideを追加しなければなりませんか?
    Scala requires such a modifier for all members thatoverride a concrete member in a parent class. The modifier is optional if amember implements an abstract member with the same name親の具体的な方法をリロードした場合、overrideは必須であり、抽象的な方法を実現した場合、overrideはオプションである
    6.別のクラスの定義
    class UniformElement(
        ch: Char,
        override val width: Int,
        override val height: Int
        ) extends Element {
      private val line = ch.toString * width
      def contents = Array.fill(height)(line)
    }

    7.重荷禁止.Javaと同様に、メソッドの前にfinalキーを付けます
    8.2つのElementを1つのElmentに上下に結合するabove方法を実現する
    abstract class Element {
      def contents: Array[String]
      def height = contents.length
      def width = if (height == 0) 0 else contents(0).length
      def above(that: Element) = new ArrayElement(this.contents ++ that.contents)
    }

    ++操作は2つのArrayをつなぐために使用される.
    aboveメソッドはArrayElementオブジェクトを返します.このことから、1つのソフトウェアシステムの各コンポーネントは相互に依存しており、ここではArrayElementがElementに依存し、ElementもArrayElementに依存している.1つのソフトウェアシステムの開発の過程で、厳格な先に誰がいて、後に誰がいるかの順序で行われたわけではありません.まず弱いAがあって、それからAの基礎の上でBがあって、それからAはまたBを使って自分をもっと強くします.ソフトウェアシステムは組み立てられているのではなく、「成長」しているのです.人間の発育過程のように、骨格と筋肉は一緒に成長し、骨格を成長させながら筋肉を成長させ、それから組み立てられています.
    9.二つのElementの左右を一つのElementに接合するためのbeside法を実現する
      def beside(that: Element): Element =  new ArrayElement (
          for (
              (line1, line2) <- this.contents zip that.contents
          ) yield line1 + line2
      )

    ここで使うzipの方法を説明します
    object Test {
      val arr1 = Array(123, 456, 78)                  //> arr1  : Array[Int] = Array(123, 456, 78)
      val arr2 = Array("aaa", "bbb", "cccc")          //> arr2  : Array[String] = Array(aaa, bbb, cccc)
      arr1 zip arr2                                   //> res0: Array[(Int, String)] = Array((123,aaa), (456,bbb), (78,cccc))
    }

    Zipの結果はan array of Tuples
    10.再ロードtoStringメソッド
    override def toString = this.contents.mkString("
    ")

    ここで、Elementクラスは次のようになります.
    abstract class Element {
      def contents: Array[String]
      def height = contents.length
      def width = if (height == 0) 0 else contents(0).length
      def above(that: Element): Element = new ArrayElement(this.contents ++ that.contents)
      def beside(that: Element): Element =  new ArrayElement (
          for (
              (line1, line2) <- this.contents zip that.contents
          ) yield line1 + line2
      )
      override def toString = this.contents.mkString("
    ") }

    11.プラントメソッドの追加
    object Element {
      def elem(contents: Array[String]) =
        new ArrayElement(contents)
      
      def elem(line: String) =
        new LineElement(line)
      
      def elem(ch: Char, width: Int, height: Int) =
        new UniformElement(ch, width, height)
    }

    12.Elementクラスをファクトリメソッドで書き換える
    import Element.elem
    
    abstract class Element {
      def contents: Array[String]
      def height = contents.length
      def width = if (height == 0) 0 else contents(0).length
      def above(that: Element): Element = elem(this.contents ++ that.contents)
      def beside(that: Element): Element =  elem (
          for (
              (line1, line2) <- this.contents zip that.contents
          ) yield line1 + line2
      )
      override def toString = this.contents.mkString("
    ")    }

    13.ElementのサブクラスをすべてElementオブジェクトのプライベートクラスとする
    given the factory methods, the subclasses ArrayElement,LineElement and UniformElement could now be private, because theyneed no longer be accessed directly by clients. In Scala, you can defineclasses and singleton objects inside other classes and singleton objects. Oneway to make the Element subclasses private, therefore, is to place them inside the Element singleton object and declare them private there. The classeswill still be accessible to the three elem factory methods, where they areneeded. Listing 10.12 shows how that will look.
    object Element {
      private class ArrayElement(
        val contents: Array[String]) extends Element
    
      private class LineElement(s: String) extends ArrayElement(Array(s)) {
        override def width = s.length()
        override def height = 1
      }
    
      private class UniformElement(
        ch: Char,
        override val width: Int,
        override val height: Int) extends Element {
        private val line = ch.toString * width
        def contents = Array.fill(height)(line)
      }
      
      def elem(contents: Array[String]): Element =
        new ArrayElement(contents)
    
      def elem(line: String): Element =
        new LineElement(line)
    
      def elem(ch: Char, width: Int, height: Int): Element =
        new UniformElement(ch, width, height)
    }

    これらのelemメソッドは、実際の戻りタイプが外部に表示されないため、戻り値タイプがElementであることを明確に示す必要があります.
    14.Elementクラスで定義されたaboveメソッドとbesideメソッドに問題があることが判明したかもしれません.なぜなら、接合後のグラフィックが矩形であることを保証できないからです.
    We need one last enhancement. The version of Element shown in Listing 10.11 is not quite sufficient, because it does not allow clients to place elements of different widths on top of each other, or place elements of differentheights beside each other.そのため、以下の改善を行う.
      def widen(w: Int): Element = {
        if (w <= width) this
        else {
          val left = elem(' ', (w - width) / 2, height)
          val right = elem(' ', width - w - left.width , height)
          left beside this beside right
        }
      }
      
      def highten(h: Int): Element = {
        if (h <= height) this 
        else {
          val top = elem(' ', width, (height - h)/2)
          val bot = elem(' ', width, height - h - top.height)
          top above this above bot
        }
      }
      
        def above(that: Element): Element = {
        val this1 = this widen that.width
        val that1 = that widen this.width
        elem(this1.contents ++ that1.contents)
      }
      def beside(that: Element): Element = {
        val this1 = this highten that.height
        val that1 = that highten this.height
        elem (
          for (
              (line1, line2) <- this1.contents zip that1.contents
          ) yield line1 + line2
        )
      }

    15.Elemen.scalaの完全なコード
    package chapter10
    import Element.elem
    
    abstract class Element {
      def contents: Array[String]
      def height = contents.length
      def width = if (height == 0) 0 else contents(0).length
      def above(that: Element): Element = {
        val this1 = this widen that.width
        val that1 = that widen this.width
        elem(this1.contents ++ that1.contents)
      }
      def beside(that: Element): Element = {
        val this1 = this highten that.height
        val that1 = that highten this.height
        elem(
          for (
            (line1, line2) <- this1.contents zip that1.contents
          ) yield line1 + line2)
      }
      override def toString = this.contents.mkString("
    ")   def widen(w: Int): Element = {     if (w <= width) this     else {       val left = elem(' ', (w - width) / 2, height)       val right = elem(' ', width - w - left.width, height)       left beside this beside right     }   }   def highten(h: Int): Element = {     if (h <= height) this     else {       val top = elem(' ', width, (height - h) / 2)       val bot = elem(' ', width, height - h - top.height)       top above this above bot     }   } } object Element {   private class ArrayElement(     val contents: Array[String]) extends Element   private class LineElement(s: String) extends ArrayElement(Array(s)) {     override def width = s.length()     override def height = 1   }   private class UniformElement(     ch: Char,     override val width: Int,     override val height: Int) extends Element {     private val line = ch.toString * width     def contents = Array.fill(height)(line)   }      def elem(contents: Array[String]): Element =     new ArrayElement(contents)   def elem(line: String): Element =     new LineElement(line)   def elem(ch: Char, width: Int, height: Int): Element =     new UniformElement(ch, width, height) }

    16.最後のステップは、私たちのコードで面白いものを描きます^^;
    package chapter10
    import Element.elem
    object TestMain {
      def main(args: Array[String]) {
         val e1 = elem('*', 5, 5)
         print(e1)
      }
    }
    
    /*  
    
    *****
    *****
    *****
    *****
    *****
    
    */
    package chapter10
    import Element.elem
    object TestMain {
      def main(args: Array[String]) {
         val coner = elem(' ', 5,5)
         val block = elem('+', 5, 5)
         val edge =  (coner beside block beside coner )
         val mid = block beside block beside block
         val cross = edge above mid above edge
         print(cross)
      }
    }
    /*
         +++++     
         +++++     
         +++++     
         +++++     
         +++++     
    +++++++++++++++
    +++++++++++++++
    +++++++++++++++
    +++++++++++++++
    +++++++++++++++
         +++++     
         +++++     
         +++++     
         +++++     
         +++++ 
    */