我们说我有这些代码:
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?
如果我使用oo / types,我可以简单地将Max定义为属性,有没有一种很好的方法来解析没有对象/类型的相同场景但只有模块/不可变绑定方式?还应该考虑一些更复杂的场景,更多可配置的数据,以不同的方式使用更多。
答案 0 :(得分:1)
因此,在我看来,您的代码旨在生成随机数学运算,然后您可以计算结果。我发现这段代码很难破译,看起来你正试图使用modules
类似面向对象的classes
,其中包含内部状态,这并不是一个考虑它们的好方法。 / p>
通过考虑较小的,可组合的代码单元,您可以实现更精细的代码重用。
以下是我对此问题的尝试:
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)
我创建了一个Range
记录来包含我将要使用的范围数据。
我的randomInRange
函数会在指定范围内生成一个随机数。
我的isInRange
函数确定给定值是否在提供的范围内。
我的randomOpGen
函数生成0-3范围内的数字,然后在随机值为1时生成Problem
:Addition
的随机类型构造函数,{{1}当2等时
(你可能想知道为什么我用Subtraction
参数定义了这个函数,而不仅仅是接受元组,答案是这样我可以让它在以后生成具有相同可能性的运算符。)
我的unit
函数通过执行适当的操作来解析算术。我修改了这个函数的结果,以便在余数为0的情况下返回calc
来处理整数除法,否则返回Some result
。所有其他计算始终返回None
。
Some result
是神奇发生的地方。
首先我生成一个随机运算符,这对于序列中的每个元素都是相同的 - 因此我前面提到的quiz
的重要性。
我从提供的范围生成一个无限的整数元组序列,并使用()
在每个元组上生成一个操作(我之前创建的那个)。
然后我使用map
来查找第一次出现的结果,该结果都在我指定的范围内且具有有效的结果值。
现在让我们试试这段代码:
Seq.find
现在让我们改变范围
let x = NumeracyProblems.quiz {Min = 1; Max = 9} x NumeracyProblems.calc x;; val x : Problem = Addition (2,7) val it : int option = Some 9
如您所见,此版本的代码与整数范围完全无关。