2016-07-05 13 views
2

Lassen Sie uns sagen, ich habe diese Code zu machen:Wie F # Module konfigurierbar (oder machen eine ähnliche Wirkung, wie Objekteigenschaften)

namespace global 
module NumeracyProblemsInTen= 
    module private Random= 
     let private a=lazy System.Random() 
     let getRandom()=a.Force() 
     let next()=getRandom().Next() 
     let lessThan exclusiveMax=getRandom().Next exclusiveMax 
     let pick seq= 
      assert(not<|Seq.isEmpty seq) 
      lessThan<|Seq.length seq|>Seq.item<|seq 
    module UsedNumbers= 
     let min,max=1,9 // *want to make these data variable* 
     let numbers={min..max} 
     let atLeast a=numbers|>Seq.skipWhile((>)a) 
     let atMost a=numbers|>Seq.takeWhile((>=)a) 
     module Random= 
      let private pick=Random.pick 
      let pickNumber()=pick numbers 
      let atMost=atMost>>pick 
    open UsedNumbers 
    module AdditionInTen= 
     module Addends= 
      let max=max-min 
      let numbers={min..max} 
      let pick()=Random.pick numbers 
     open Addends 
     let quiz()= 
      let addend=pick() 
      addend,Random.atMost<|min+max-addend 
     let calc(addend,another)=addend+another 
    module MultiplyInTen= 
     let quiz()= 
      let multipiler=Random.pickNumber() 
      multipiler,Random.pick{min..max/multipiler} 
     let calc(multipiler,another)=multipiler*another 
    module SubtractionInTen= 
     let minSubtrahend,minResult=min,min 
     let minMinuend=minSubtrahend+minResult 
     let minuends=atLeast minMinuend 
     let quiz()= 
      let minuend=Random.pick minuends 
      minuend,Random.pick{minSubtrahend..minuend-minResult} 
     let calc(minuend,subtrahend)=minuend-subtrahend 
    module DeviditionInTen= 
     let devisible devidend deviser=devidend%deviser=0 
     let findDevisers devidend=numbers|>Seq.filter(devisible devidend) 
     let findDeviditions devidend=findDevisers devidend|>Seq.map(fun deviser->devidend,deviser) 
     let problems=Seq.collect findDeviditions numbers 
     let quiz()=Random.pick problems 
     let calc(devidend,deviser)=devidend/deviser 
    type Problem=Addition of int*int|Subtraction of int*int|Multiply of int*int|Devidition of int*int 
    let quiz()= 
     let quizers=[AdditionInTen.quiz>>Addition;SubtractionInTen.quiz>>Subtraction; 
      MultiplyInTen.quiz>>Multiply;DeviditionInTen.quiz>>Devidition] 
     quizers|>Random.pick<|() 
    let calc problem= 
     match problem with 
      |Addition(addend,another)->AdditionInTen.calc(addend,another) 
      |Subtraction(minuend,subtrahend)->SubtractionInTen.calc(minuend,subtrahend) 
      |Multiply(multipiler,another)->MultiplyInTen.calc(multipiler,another) 
      |Devidition(devidend,deviser)->DeviditionInTen.calc(devidend,deviser) 
module NumeracyProblemsUnderOneHundred= 
    module UsedNumbers= 
     let min,max=1,99 
     // ... 
    // ... 
    // ... 
    // OMG! Do I must copy all the previous code here? 

Wenn ich oo/Typen, ich kann einfach definieren Max als Eigenschaft ist gibt es eine gute Möglichkeit, die gleiche Szene ohne Objekt/Typen aufzulösen, aber nur Module/unveränderliche Bindungen? Ein bisschen komplexere Szene sollte ebenfalls in Betracht gezogen werden, mehr konfigurierbare Daten, mit mehr Nutzung auf verschiedene Arten.

+2

Ist ein [Funktor] (http://stackoverflow.com/q/2030863/1243762) was Sie suchen? F # hat sie nicht. –

+3

Überhaupt nicht klar, was Sie hier zu tun versuchen. Überlegen Sie, Ihre Frage neu zu formulieren. –

+0

@FyodorSoikin: –

Antwort

1

So scheint es mir, dass Ihr Code entworfen ist, um eine zufällige mathematische Operation zu generieren, die Sie dann das Ergebnis berechnen können. Ich fand diesen Code ziemlich schwierig zu entziffern, es scheint, dass Sie versuchen, modules wie objektorientierten classes zu verwenden, die internen Zustand enthalten, und das ist nicht wirklich eine gute Möglichkeit, über sie nachzudenken.

Sie können viel mehr granulare Codewiederverwendung erreichen, indem Sie an kleinere, zusammensetzbare Codeeinheiten denken.

Hier ist mein Versuch, dieses Problem:

type Range = {Min : int; Max : int} 

type Problem= 
    |Addition of int*int 
    |Subtraction of int*int 
    |Multiplication of int*int 
    |Division of int*int 

module NumeracyProblems = 
    let private rnd = System.Random() 

    let randomInRange range = rnd.Next(range.Min, range.Max+1) 

    let isInRange range x = x >= range.Min && x <= range.Max 

    let randomOpGen() = 
     match randomInRange {Min = 0; Max = 3} with 
     |0 -> Addition 
     |1 -> Subtraction 
     |2 -> Multiplication 
     |3 -> Division 

    let calc = function 
     |Addition (v1, v2) -> Some(v1 + v2) 
     |Subtraction (v1, v2) -> Some(v1 - v2) 
     |Multiplication (v1, v2) -> Some(v1 * v2) 
     |Division (v1, v2) -> 
      match v1 % v2 = 0 with 
      |true -> Some(v1/v2) 
      |false -> None 

    let quiz range = 
     let op = randomOpCtor() 
     let optionInRange x = 
      match isInRange range x with 
      |true -> Some x 
      |false -> None 
     Seq.initInfinite (fun _ -> randomInRange range, randomInRange range) 
     |> Seq.map (op) 
     |> Seq.find (Option.isSome << Option.bind (optionInRange) << calc) 

ich einen Range Datensatz erstellt haben die Bereichsdaten enthalten Ich werde arbeiten mit.

Meine randomInRange Funktion generiert eine Zufallszahl innerhalb des angegebenen Bereichs.

Meine isInRange Funktion bestimmt, ob ein bestimmter Wert innerhalb des angegebenen Bereichs liegt.

Meine randomOpGen Funktion eine Zahl im Bereich von 0-3 erzeugt und erzeugt dann ein zufälliges Typkonstruktor für Problem: Addition wenn der Zufallswert 1 ist, Subtraction wenn 2 usw.

(Sie fragen sich vielleicht, warum Ich habe diese Funktion mit einem unit Argument definiert, anstatt nur das Tupel zu akzeptieren, die Antwort ist, damit ich Operatoren mit gleicher Wahrscheinlichkeit später erzeugen kann.)

Meine calc Funktion löst die Arithmetik durch Ausführen der entsprechenden Operation . Ich habe das Ergebnis dieser Funktion so geändert, dass es die Ganzzahldivision behandelt, indem es Some result für Fälle zurückgibt, in denen der Rest 0 und None ist. Alle anderen Berechnungen geben immer Some result zurück.


quiz ist, wo die Magie passiert.

Zuerst erzeuge ich einen Zufallsoperator, dies wird für jedes Element in der Folge später gleich sein - daher die Wichtigkeit der (), die ich bereits erwähnt habe.

Ich erzeuge eine unendliche Folge von Integer-Tupeln aus dem angegebenen Bereich und erzeuge mithilfe von map eine Operation (die ich zuvor erstellt habe) für jedes dieser Tupel.

Dann verwende ich Seq.find, um das erste Vorkommen eines Ergebnisses zu finden, das innerhalb des angegebenen Bereichs liegt und einen gültigen Ergebniswert hat.


Nun wollen wir diesen Code versuchen:

let x = NumeracyProblems.quiz {Min = 1; Max = 9} 
x 
NumeracyProblems.calc x;; 
val x : Problem = Addition (2,7) 
val it : int option = Some 9 

Nun wollen wir den Bereich

ändern
let x = NumeracyProblems.quiz {Min = 1; Max = 99} 
x 
NumeracyProblems.calc x 
val x : Problem = Division (56,2) 
val it : int option = Some 28 

Wie Sie sehen können, ist diese Version des Codes ist völlig agnostisch zu th e ganzzahliger Bereich.

+0

Das Leben des Seins auf SO ist die Anstrengung aber in eine Antwort umgekehrt proportional zur Rückkehr in Punkte. –

+0

@GuyCoder Es ist wahrscheinlich, weil ich nicht sofort geantwortet habe. Niemand sieht sich die Fragen an, wenn sie die Liste noch einmal durchschritten haben. – TheInnerLight

+0

Also wahr ........ –