ScalaCheckに぀いお。 ゞェネレヌタヌパヌト2

パヌト2.ゞェネレヌタヌ


このシリヌズの入門蚘事で、ゞェネレヌタヌを既に知っおいるこずを願っおいたす。 このチュヌトリアルでは、孊習した知識を統合し、独自の再垰を含むゞェネレヌタヌの䜜成方法を孊習したす。 ゞェネレヌタヌ専甚ですが、プロパティに぀いおも忘れたせん。 さらに、それらを積極的に䜿甚しお、ゞェネレヌタヌメカニズムのフルパワヌを実蚌したす。 前提条件のメカニズムを怜蚎しおください。 おそらく、シリヌズの2番目の蚘事をプロパティに圓おる方が論理的であり、おそらくこれが正しい決定でしょう。 しかし、私の個人的な芳察によるず、最も困難なのはゞェネレヌタヌです。 次の蚘事でプロパティを怜蚎したす。


サむクル構造



発電機


PropオブゞェクトのforAllメ゜ッドは、ランダムに生成されたデヌタを䜿甚したす。 単玔なデヌタ型たたはコンテナ 耇合 型のいずれかです。 ScalaCheckでは、ゞェネレヌタヌはGenの子孫です。


 sealed abstract class Gen[+T] { def apply(prms: Gen.Params): Option[T] ... } 

クラスは、生成される倀のタむプによっおパラメヌタヌが決たりたす。 したがっお、文字列にはGen[String]があり、悪名高いPerson - Gen[Person]たす。 必芁な圱響が範囲内であれば、 Arbitraryオブゞェクトのarbitraryメ゜ッドを䜿甚するこずもできたす。


ゞェネレヌタヌに暗黙的なサポヌトを远加したす


暗黙のこずを話しおいるので、 arbitrary
あなたのタむプに。 たず、型自䜓が必芁です。


 sealed trait LED case object Red extends LED case object Green extends LED case object Blue extends LED 

次に、ダむオヌド甚のゞェネレヌタヌを䜜成したす。


 val ledGenerator: Gen[LED] = Gen.oneOf(Red, Green, Blue) 

ここで、ゞェネレヌタヌからimplicit Arbitraryむンスタンスを䜜成したす。


 implicit val arbitraryLed: Arbitrary[LED] = Arbitrary(ledGenerator) Arbitrary.arbitrary[LED].sample // Some(Green) 

これで、スコヌプ内にLEDがあるので、いく぀かのプロパティを確認できたす。


 val ledProp = forAll { diode: LED => //    } 

倚数のプリミティブ型ず暙準ラむブラリのコンテナの堎合、暗黙的な倉換はArbitraryオブゞェクト内ですでに事前定矩されおいたす。


 import org.scalacheck.Arbitrary.arbitrary val arbitraryString = arbitrary[String] val arbitraryInteger = arbitrary[Int] 

Gen.resultOfは、この問題であなたを倧いに助けるこずができたす。 この関数は、 arbitrary倀が既に定矩されおいるコンストラクタヌパラメヌタヌに察しお、ケヌスクラスを受け入れたす。


 case class Coord(x: Double, y: Double) //     resultOf. val genCoord = Gen.resultOf(Coord) //    Arbitrary. implicit val arbitraryCoord = Arbitrary(genCoord) //         . val prop = Prop.forAll { coord: Coord => //... } 

sample方法


残念ながら、 toStringメ゜ッドはGenクラスのむンスタンスに察しお定矩されおいたせんが、より䟿利で䟿利なメ゜ッドがありたす。 ゞェネレヌタヌを䜜成するずきには、非垞に頻繁に䜿甚する必芁さえありたす。 これはGen.sampleメ゜ッドです。 Option内で生成された倀の䟋を返したす。


 // ScalaCheck     ,   //  10 . Arbitrary.arbitrary[String].sample map (_ take 10) //     . // Some(冋执韭蜙⮻拍箘㖕) Arbitrary.arbitrary[Double].sample // Some(-5.180668081211655E245) 

倉換


前のパヌトですでに説明したように、 mapメ゜ッドはゞェネレヌタヌにも適甚できたす。 これを次の䟋で説明したす。


 val octDigitStr = choose(0, 7) map (_.toString) octDigitStr.sample // Some(3) octDigitStr.sample // Some(5) 

フィルタリング


ゞェネレヌタヌ甚のfilterメ゜ッドもありたすが、実際には、これはsuchThatメ゜ッドを呌び出すための単なる゚むリアスです。


 val evenOct = octDigit.filter(_ % 2 == 0) evenOct.sample // None //  ,   : evenOct.sample // None //  ,    ? evenOct.sample // Some(2) 

䞊蚘の䟋は、 filterやsuchThat䜿甚しない理由を瀺しおいたす。 新しいゞェネレヌタヌは、フィルタヌを通過しない倀を単に拒吊したす。 これにより、テストプロセスが倧幅に遅くなりたす。


前提条件はフィルタヌず同様に機胜したす。

前提条件


ScalaCheckの含意操䜜によっおロゞックで提瀺される前提条件は、プロパティ内で䜿甚されるメ゜ッドです。 入力を制限するこずができたす。 挔算子==>によっお曞かれたした。 前提条件を䜿甚するには、 Prop.propBooleanに远加する必芁がありProp.propBoolean 。 したがっお、偶数の堎合、最埌の桁も偶数になるずいうステヌトメントを確認したしょう。


 import org.scalacheck.Prop.{forAll, propBoolean} def isEven(number: Int) = number % 2 == 0 //    . def lastChar(number: Int) = number.toString.last.toInt // . val p2 = forAll(arbitrary[Int] suchThat isEven) { number => isEven(lastChar(number)) } //  . val p1 = forAll(arbitrary[Int]) { number => isEven(number) ==> isEven(lastChar(number)) } 

プロパティを開始するには、 check機胜を䜿甚できたす。


 //     p1.check // + OK, passed 100 tests. //   p2.check // + OK, passed 100 tests. 

フィルタヌが硬いほど、より倚くの倀が無芖されたす。 これは、倀の小さなセットたたは互いに非垞に離れおいる倀の堎合に重芁になるこずがありたす。 たずえば、範囲Intの玠数。 このような取り組みから、ScalaCheckは簡単に疲れお、あなたに降䌏するこずができたす。


 scala> p1.check ! Gave up after only 1 passed tests. 100 tests were discarded 

偶数の䟋ではこれに気付かないでしょうが、単玔な数の䟋では簡単です。 次のこずも知っおおく必芁がありたす。


偶数のフィルタヌはさたざたな方法で実装できたす。 たずえば、あなたは取るこずができたす
任意の敎数で、2を掛けたす。玠数の堎合
事前にカりントする方がはるかに簡単です。たずえば、
゚ラトステネスのふるい 、そしおリストを突き刺す
Gen.oneOf内の蚈算倀。これに぀いおは埌で説明したす
蚘事。

suchThatを䜿甚しおゞェネレヌタヌをsuchThat 、問題も発生する可胜性がありたす。 そのような堎合は、 retryUtilメ゜ッドを䜿甚する必芁がありたす。 suchThatずは異なり、ScalaCheckの動䜜が難しくなり、無限ルヌプに陥るこずがありたす。 retrtyUtilがretrtyUtil fiterたたはsuchThat䌌おいsuchThat 


 arbitrary[Int] retryUntil isEven 

たた、 fromOption flattenずflatten 、およびfromOptionがDeprecatedず宣蚀されおいるこずにfromOptionかもしれたせん。 これは、空のゞェネレヌタヌを䜜成する誘惑を少なくするために行われたす。


独自のゞェネレヌタヌを䜜成する


前述のように、既存のゞェネレヌタヌを理解のために組み合わせるこずで、独自のゞェネレヌタヌを䜜成できたす。


 val coordGen = for { i <- arbitrary[Int]; j <- arbitrary[Int] } yield (i, j) coordGen.sample // Option[(Int, Int)] = Some((45,60)) coordGen.sample // Option[(Int, Int)] = Some((29, 37)) 

任意のデヌタ構造のゞェネレヌタヌを宣蚀できたす。 ADTを䜿甚する単玔なケヌス


 sealed trait LightColor case object Red extends LightColor case object Orange extends LightColor case object Green extends LightColor case object FlashingGreen extends LightColor case object Extinct extends LightColor val colorGen = Gen.oneOf(Red, Orange, Green, Extinct) 

人生を耇雑にしおいるなら、培底的に


 //       ,     . type Color = LightColor object PedestrianTrafficLight { sealed class State(_1: Color, _2: Color) case object Stop extends State(Red, Extinct) case object Move extends State(Extinct, Green) case object FinishMove extends State(Extinct, FlashingGreen) def states: Seq[State] = Seq(Move, FinishMove, Stop) } object VehicularTrafficLight { sealed class State(_1: Color, _2: Color, _3: Color) case object Stop extends State(Red, Extinct, Extinct) case object Ready extends State(Red, Orange, Extinct) case object Move extends State(Extinct, Extinct, Green) case object Warn extends State(Red, Orange, Extinct) case object FinishMove extends State(Extinct, Extinct, FlashingGreen) def states: Seq[State] = Seq(Stop, Ready, Move, Warn, FinishMove) } sealed trait TrafficLight case class PedestrianTrafficLight(state: PedestrianTrafficLight.State) extends TrafficLight case class VehicularTrafficLight(state: VehicularTrafficLight.State) extends TrafficLight 

ゞェネレヌタヌを宣蚀したす。


 import Gen._ //      val pedestrianTrafficLightStateGen = Gen.oneOf(PedestrianTrafficLight.states) //      val vehicularTrafficLightStateGen = Gen.oneOf(VehicularTrafficLight.states) 

そしお、歩行者ず亀通信号からタプルを生成したす。 䞀぀だけありたす信号機の状態は盞互に排他的であっおはなりたせん。 たず、歩行者が操䜜できる条件を決定したす。 合成によっおゞェネレヌタヌを䜜成するため、 retryUntil代わりにsuchThatを䜿甚したす。


 val pedestrianCanGo = pedestrianTrafficLightStateGen.retryUntil { state => state != PedestrianTrafficLight.Stop } 

次に、信号機の状態を生成し、それから始めお、歩行者の蚱容状態を決定し、それらをリストに結合したす。 受け取ったゞェネレヌタヌのタむプは、 Gen[(VehicularTrafficLight.State, PedestrianTrafficLight.State)]です。


 val states = vehicularTrafficLightStateGen flatMap { case vehState @ VehicularTrafficLight.Stop => pedestrianCanGo map (pedState => (vehState, pedState)) case pedestrianCanNotGo => (pedestrianCanNotGo, PedestrianTrafficLight.Stop) } 

オブゞェクト自䜓に信号機のステヌタスを衚瀺したす


 val trafficLightPair = states map { case (v, p) => (VehicularTrafficLight(v), PedestrianTrafficLight(p)) } 

結果を確認したすか 私は受け取った


 (VehicularTrafficLight(FinishMove),PedestrianTrafficLight(Stop)) 

そしお


 (VehicularTrafficLight(Move),PedestrianTrafficLight(Stop)) 

それは党く本圓です。 倚分どこか私が間違っおいた。 䞻なこずは、メカニズム自䜓を実蚌するこずでした。


ScalaCheckでは、再垰的なデヌタ構造を生成するこずもできたす。
ただし、このプロセスを簡単か぀些现なこずず呌ぶこずは困難です。 さらにこれで
この蚘事では、再垰ゞェネレヌタヌず、その問題に぀いお芋おいきたす。
それらを䜿甚するずきに遭遇するかもしれたせん。

プロパティのゞェネレヌタヌ


前述のほずんどの䟋では、ScalaCheckはゞェネレヌタヌの適切なむンスタンスを個別に遞択し、それを䜿甚しおプロパティを蚈算したす。 ただし、ゞェネレヌタヌを独自に䜜成したのはこのためではなく、ScalaCheckはスコヌプから䜕かを削陀したす。 「互換性のある」信号機を芚えおいたすか それらをどこかにプッシュしたしょう


 import Prop.{forAll, propBoolean} def isRed(trafficLight: VehicularTrafficLight) = trafficLight.state == VehicularTrafficLight.Stop def notRed(trafficLight: PedestrianTrafficLight) = trafficLight.state != PedestrianTrafficLight.Stop //        Prop.forAll val canNotBothBeRed = forAll(trafficLightPair) { case (vehicular, pedestrian) => isRed(vehicular) ==> notRed(pedestrian) } 

チェックしおみお


 canNotBothBeRed.check 

そしお、すべおが正垞であるこずを確認したす。


 + OK, passed 100 tests. 

倚くの堎合、1぀のゞェネレヌタヌでは十分ではないため、耇数のゞェネレヌタヌを䜿甚できたす。


 val p = forAll(Gen.posNum[Int], Gen.negNum[Int]) { (pos, neg) => neg < pos } 

ゞェネレヌタヌなどのプロパティは、䞀緒にネストできるため、䞊蚘の䟋を次のように曞き換えるこずができたす。


 val p = forAll(Gen.posNum[Int]) { pos => forAll(Gen.negNum[Int]) { neg => neg < pos } } 

ネストされたforAll呌び出しの代わりに、䜿甚するゞェネレヌタヌからタプルをコンパむルするこずもできたす。 たた、 forAll 2回呌び出さないでください。


発電機のラベル


タグを䜿甚するこずをお勧めしたす。 これにより、゚ラヌ報告が改善され、ゞェネレヌタヌを䜿甚するコヌドの可読性が向䞊したす。 ラベルを远加するには、 |:および:|䜿甚できたす:| 。 ラベルは、文字列たたは文字のいずれかです。 耇数のタグをゞェネレヌタに远加できたす。 説明するために、前の䟋を䜿甚しお、 蚘号を倉曎し、プロパティを䞍条理にしたす。


 //  |:  :|   . val p = forAll('positive |: Gen.posNum[Int]) { pos => forAll(Gen.negNum[Int] :| "negative numbers") { neg => neg > pos } } 

同意しお、良くなった


 ! Falsified after 0 passed tests. > positive: 0 > positive_ORIGINAL: 1 > negative numbers: 0 

プリミティブゞェネレヌタヌ


定数


Gen.constよりも簡単なものをGen.constはGen.constです。 任意の倀を取り、ゞェネレヌタヌに泚意深くパックしたす。 適切なタむミングで適切な堎所にあるオブゞェクトは、暗黙的にGenキャストされたすGen したがっお、ほずんどの堎合、このメ゜ッドを明瀺的に呌び出す必芁はありたせん。


死ぬ


ScalaCheckは、実行䞍可胜なゞェネレヌタヌを返すGen.failメ゜ッドを䜿甚したす。 sampleメ゜ッドを呌び出そうずするず、垞にNoneたす。 ほずんどの堎合、圌はあなたの方法で䌚うこずはありたせん、圌が䌚った堎合、あなたは圌に粟通しおいるこずを考慮しおください


 Gen.fail.sample // Option[Nothing] = None 

数字


Gen.posNumずGen.negNumを䜿甚しお、それぞれ正の数ず負の数を生成できたす。


 import org.scalacheck.Gen import org.scalacheck.Prop.forAll val negative = Gen.negNum[Int] val positive = Gen.posNum[Int] val propCube = forAll (negative) { x => x * x * x < 0 } 

Gen.chooseNumには興味深い機胜がありたすこのゞェネレヌタヌは、指定された範囲包括的で、れロ指定された範囲内に収たる堎合、最小倀ず最倧倀、および提瀺された倀のリストの重みで数倀を生成したす


 //  specials  vararg. Gen.chooseNum(minT = 2, maxT = 10, specials = 9, 5) 

Gen.chooseを䜿甚しお、特定の範囲内の数倀を生成できたす。 さらに、これは決しお倱敗しないたれなタむプのゞェネレヌタヌです。 シンボルでも䜿甚できたす。 キャラクタヌゞェネレヌタヌずいえば...


キャラクタヌ


ScalaCheckには倚くの文字ゞェネレヌタヌがありたす。 次から遞択するように招埅されたす。



それでは、「海戊」でゲヌムの座暙を生成しおみたしょう。


 val coord = for { letter: Char <- Gen.alphaUpperChar number: Char <- Gen.numChar } yield s"$letter$number" coord.sample // Some(L1) 

理解のおかげで、リスト、タプル、文字列などの任意の集蚈を生成できたす。 行ずいえば...


行


簡単に生成するこずもできたす。 Gen.alphaStrは、アルファベット文字からのみ文字列を生成したす。 Gen.numStrは数倀文字列を生成したす。 Gen.alphaLowerStrずGen.alphaUpperStrが異なる文字から文字列を個別に生成するこずができたすGen.alphaLowerStrずGen.alphaUpperStrは、これらの目的のために蚭蚈されおいたす。 Gen.idenifierは、垞に小文字のアルファベット文字で始たり、その埌に英数字が続く空でない文字列を提䟛したす。 䞀郚の文法では、これは非垞に識別子です。


 import org.scalacheck.Gen val stringsGen = for { key <- Gen.identifier value <- Gen.numStr } yield Bucket(key take 8, value take 2) stringsGen.sample // Some(Bucket(kinioQqg,60)) 

機胜


Scalacheckは関数 Function0むンスタンス () => T も生成できたす。 これを行うには、戻り倀を生成するゞェネレヌタヌをGen.function0たす。


 val f = Gen.function0(Arbitrary.arbitrary[Int]).sample //  tostring  ,      - : // some(org.scalacheck.gen$$$lambda$40/1418621776@4f7d0008) 

コンテナ発電機


arbitraryを䜿甚しお、暙準コレクションのむンスタンスを生成できたす。


 import Arbitrary.arbitrary val listgen = arbitrary[List[Int]] val optgen = arbitrary[Option[Int]] 

しかし、これは倚くの堎合十分ではないため、ScalaCheckはコレクションずその生成を操䜜するための高床なツヌルを提䟛したす。 次に、 sampleメ゜ッドによっお返されるOptionを省略し、誀解を招かないように、すぐに倀を衚瀺したす。


生成オプション


Gen.someを䜿甚するず、「収入」が保蚌されたす。


 //     : Gen.some("") // Some() 

さらに悪いこずに、お金に远加の自由床がある堎合


 // . Gen.option("") // None 

リストず蟞曞を生成したす


Gen.listOfは任意の長さのリストを生成し、 Gen.listOfNは指定された長さのリストを生成したす。


 //  3     Gen[Int]. Gen.listOf(3) map (_ take 5) // List(3, 3, 3, 3, 3) //    . Gen.listOfN(5, Gen.posNum[Double]) map (_ take 5) 

Gen.listOfゞェネレヌタヌは空のリストを返す堎合がありたす。 空でないリストが必芁であるこずが保蚌されおいる堎合は、 Gen.nonEmptyListOfを䜿甚できたす。 蟞曞たたはマップ Map を生成するには、同様のメ゜ッドがありたす Gen.mapOf 、 Gen.mapOfN 、およびGen.nonEmptyMapOfです。 リストメ゜ッドずは異なり、蟞曞ゞェネレヌタヌには、2芁玠のタプルゞェネレヌタヌが必芁です。


 import Arbitrary._ val tupleGen = for { i <- arbitrary[Short] j <- arbitrary[Short] } yield (i, j) Gen.mapOfN(3, tupleGen) map (_ take 3) // Map(10410 -> -7991, -19269 -> -18509, 0 -> -18730) 

特定のセットの芁玠のリストを生成したす


Gen.someOfは、指定したセットに含たれる特定の芁玠セットからArrayBufferを返したす。次に䟋を瀺したす。


 import org.scalacheck.Gen.someOf val numbers = someOf(List(1, 2, 3, 4, 5)).sample // Some(ArrayBuffer(5, 4, 3)) val leds = someOf(List(Red, Green, Blue)).sample // Some(ArrayBuffer(Blue, Green)) 

Gen.pickず同様にGen.someOfたす。 唯䞀の違いは、 Gen.pick䜿甚するず、必芁な芁玠数を指定できるこずです。


 import org.scalacheck.Gen.pick val lettersGen = Gen.pick(2, List('a', 'e', 'i', 'o', 't', 'n', 'm')).sample // Some(ArrayBuffer(m, n)) 

シヌケンスを生成する


Gen.sequenceは、受け入れられた倀のシヌケンスに基づいおArrayListを䜜成したす。 提䟛されたゞェネレヌタヌの少なくずも1぀が該圓する堎合、シヌケンス党䜓が該圓したす。


 import Gen.{const, choose} val ArrayListGen = Gen.sequence(List(const('a'), const('b'), choose('c', 'd'))) // [a, b, d] 

無限のストリヌムを生成したす


ScalaCheckは、無限ストリヌムを生成する機胜をサポヌトしおいたす。


 val stream = Gen.infiniteStream(Gen.choose(0,1)) //   8  stream.sample map (stream => stream.take(8).toList) // Some(List(1, 0, 0, 0, 1, 1, 1, 0)) 

コンテナ発電機


蟞曞ずリストの䞊蚘のメ゜ッドは、実際には、 Gen.containerOfずそのパヌトナヌを䜿甚する特殊なケヌスです containerOfNずnonEmptyContainerOf`。 これは、私たちが知っおいるほずんどのコレクションを生成できる最も䞀般的な圢匏です。 次の䟋を芋おみたしょう。


 import Arbitrary._ //   «»     . val prettyShortSeq = Gen.containerOfN[Seq, Short](3, arbitrary[Short]) // Vector(0, -24114, 32767) //  :  . val genNonEmptyList = Gen.nonEmptyContainerOf[Map[K, V], (K, V)] (oneOf("foo", "bar")) 

containerOf䜜成されたゞェネレヌタヌは、型ではなく抜象型で䜜成されたす。 containerOfメ゜ッドのシグネチャを芋おみたしょう。


 def containerOf[C[_],T](g: Gen[T])(implicit evb: Buildable[T,C[T]], evt: C[T] => Traversable[T] ): Gen[C[T]] = 

そしおその実装に぀いお


 buildableOf[C[T],T](g) 

したがっお、リストたたは* ⟶ *ずしお衚すこずができる䜕かを生成する必芁がある堎合、 containerOfが圹立ちたす。 そしお、 * ⟶ * ⟶ *ようなものたずえばMap を生成する必芁がある堎合は、 * ⟶ * ⟶ *実装で芋るこずができるさらに抜象的なメ゜ッドを䜿甚する必芁がありたす。


 def mapOf[T,U](g: => Gen[(T,U)]) = buildableOf[Map[T,U],(T,U)](g) 

containerメ゜ッドず同様に、メ゜ッドnonEmptyBuildableOf 、 nonEmptyBuildableOfおよびnonEmptyBuildableOfたす。 通垞、Scalacheckは、枡されたコレクションを型ずしお構築する方法を決定したす。 これを行うために、タむプorg.scalacheck.util.Buildable暗黙的なむンスタンスを䜿甚したす。 このようなむンスタンスは、すべおの暙準コレクションタむプのScalaCheckで宣蚀されたす。 Arbitraryむンスタンスず同様に、コレクションを実装しおcontainerOfサポヌトを取埗するのは簡単です。


高い発電機


芪愛なる読者の皆さん、 高階の機胜は䜕か知っおいるず思いたす。 ゞェネレヌタヌにも同様の動䜜がありたす。ゞェネレヌタヌは、他のゞェネレヌタヌを匕数ずしお受け取り、他のゞェネレヌタヌを生成できたす。 たれに䟋倖を陀いお、以前に怜蚎したコンテナヌゞェネレヌタヌも高次ゞェネレヌタヌです。


倚くの䞀぀


Gen.oneOfは2からNゞェネレヌタヌを受け取るこずができ、それによっお新しいゞェネレヌタヌを生成したす。


 import Gen.{oneOf, const, choose} //      Gen[T] val dogies = oneOf("Lucy", "Max", "Daisy", "Barney") //        val windows = oneOf(choose(1, 8), const(10)) 

oneOf 、リストなどのscala.collection.Seq枡すこずができたす。


呚波数発生噚


Gen.frequencyは、入力ずしおタプルのリストを受け入れたす。 それぞれがゞェネレヌタヌずその確率的重みで構成されたす。 出力は、枡された敎数倀を確率の重みずしお䜿甚する新しいゞェネレヌタヌです。


 val russianLettersInText = Gen.frequency ( (9, ''), (9, ''), (8, ''), (7, ''), (6, '') //..   ) 

レむゞヌゞェネレヌタヌ


Gen.lzyを䜿甚するず、本圓に必芁になるたで生成が遅れたす。 合成ゞェネレヌタヌや、ASTなどの再垰的なデヌタ構造の生成に非垞に圹立ちたす。 Gen.lzyを䜿甚する堎合、蚈算は1回実行されたす。 Gen.delay蚈算もGen.delayが、毎回実行されたす。


発電機サむズ


Gen.sizeは、唯䞀のパラメヌタヌずしおラムダを取り、パラメヌタヌずしお敎数を取りたす。 この数倀は、ScalaCheckが実行時に瀺すサむズです。 Gen.resize䜿甚Gen.resizeず、必芁に応じおゞェネレヌタヌのサむズを蚭定できGen.resize 。 これを次の䟋で説明したす。


 def genNonEmptySeq[T](genElem: Gen[T]): Gen[Seq[T]] = Gen.sized { size => for { listSize <- Gen.choose(1, size) list <- Gen.containerOfN[Seq, T](listSize, genElem) } yield list } val intVector = genNonEmptySeq(Arbitrary.arbitrary[Int]) val p = Prop.forAll(Gen.resize(5, intVector)) { list => list.length <= 5 } 

チェック


 p.check // + OK, passed 100 tests. 

Gen.resizeは、既存のGen.resizeれたものに基づいお新しいゞェネレヌタヌを䜜成しGen.resize 。 これは、再垰ゞェネレヌタヌを䜜成するずき、および合成を䜿甚しお単玔なゞェネレヌタヌから耇雑なゞェネレヌタヌを䜜成するずきに䟿利です。


フラクタルゞェネレヌタ


倚くの堎合、再垰的なデヌタ構造、぀たりASTを䜿甚する必芁がありたす。 . , , , . :


 trait IntTree case class Leaf (value: Int) extends IntTree case class Node (children: Seq[IntTree]) extends IntTree 

:


 import org.scalacheck.Gen._ def treeGen: Gen[IntTree] = oneOf(leafGen, nodeGen) def leafGen: Gen[Leaf] = arbitrary[Int] map (value => Leaf(value)) def nodeGen: Gen[Node] = listOf(treeGen) map (children => Node(children)) 

:


 treeGen.sample Exception in thread "main" java.lang.StackOverflowError at org.scalacheck.ArbitraryLowPriority$$anon$1.arbitrary(Arbitrary.scala:70) at org.scalacheck.ArbitraryLowPriority.arbitrary(Arbitrary.scala:74) at org.scalacheck.ArbitraryLowPriority.arbitrary$(Arbitrary.scala:74) at org.scalacheck.Arbitrary$.arbitrary(Arbitrary.scala:61) 

treeGen . . . treeGen :


 def treeGen: Gen[IntTree] = lzy(oneOf(leafGen, nodeGen)) 

:


 treeGen.sample // Some(Leaf(857998833)) // Some(Leaf(2147483647)) // Some(Leaf(489549235)) 

画像


 Exception in thread "main" java.lang.StackOverflowError at org.scalacheck.rng.Seed.next(Seed.scala:17) at org.scalacheck.rng.Seed.long(Seed.scala:39) at org.scalacheck.Gen$Choose$.org$scalacheck$Gen$Choose$$chLng(Gen.scala:309) at org.scalacheck.Gen$Choose$$anon$5.$anonfun$choose$1(Gen.scala:358) at org.scalacheck.Gen$$anon$3.doApply(Gen.scala:254) at org.scalacheck.Gen.$anonfun$map$1(Gen.scala:75) at org.scalacheck.Gen$$anon$3.doApply(Gen.scala:254) at org.scalacheck.Gen.$anonfun$flatMap$2(Gen.scala:80) at org.scalacheck.Gen$R.flatMap(Gen.scala:242) at org.scalacheck.Gen$R.flatMap$(Gen.scala:239) 

, , . . Gen.sized Gen.resize :


 def nodeGen: Gen[Node] = sized { size => choose(0, size) flatMap { currSize => val nGen = resize(size / (currSize + 1), treeGen) listOfN(currSize, nGen) map (child => Node(child)) } } 

:


 val nGen = resize(size / (currSize + 1), treeGen) 

より小さなサむズの新しいゞェネレヌタヌを䜜成したすネストのレベルに比䟋しおサむズが瞮小されたす。特定のNに察しおサむズ0の空のゞェネレヌタヌが䜜成され、スタックオヌバヌフロヌは発生したせん。ゞェネレヌタヌは、ネストのレベルがわからないため、毎回ゞェネ​​レヌタヌのサむズを小さくする必芁がありたす。


そこで、再垰的なゞェネレヌタヌを芋぀けたした。JSONのAST生成の䟋は、Eric Torreborrespecs2の䜜成者ブログにありたす。


これで、ゞェネレヌタヌずの関係を終了し、プロパティに進みたす。これらの詳现に぀いおは、シリヌズの次の蚘事で説明したす。じゃあね



Source: https://habr.com/ru/post/J320104/


All Articles