在编写编程语言代码之前对问题有30000英尺的视角?

时间:2018-10-10 21:39:41

标签: alloy

最近,我有机会辅导正在参加入门级大学计算机科学课程的人员。这堂课正在学习用Java编程。这个人对课程书中的一个Java程序有一些疑问。 Java程序的目的是模拟掷硬币多次,然后报告正面数和反面数。本书展示了一个带有私有和公共部分的Java类,一个类构造函数,“ main”使用了固定变量和“ for”循环。我认为我已经成功地解释了这些Java构造的复杂性,但这使我想知道这是否真的是引入计算机科学的最佳方法?

我有几个问题:

  1. 在编写编程语言代码之前创建模型是一个好主意吗?

  2. 我(下面)30,000英尺的抛硬币问题描述清楚吗?任何部分令人困惑?有错别字吗?

  3. 这篇文章的底部是我创建的Alloy模型。清楚吗?我展示两个断言。它们有用吗?您能想到其他有用的断言吗?

这是我的文章:

就我个人而言,在以某种编程语言实现程序之前,我希望对问题有30,000的看法:在被编程语言的特质淹没之前,我希望了解问题的实质。让我用掷硬币的例子来说明这个30,000英尺的透视图。

使用一枚硬币。硬币有两个面,头和尾。这是“面部”两类的图形说明:

face category

有一组抛硬币:{抛硬币0,抛硬币1,抛硬币2,...}

我特意选择了“ set”一词。我的意思是数学意义上的“设定”:没有重复,顺序无关紧要。这表明,如果有足够的CPU(或足够的人员),所有抛硬币都可以并行进行。抛硬币的顺序无关紧要!

抛硬币的结果是一张脸。也就是说,硬币,面子和抛硬币之间存在关系。

coin tosses relation

例如,假设第一个抛硬币的结果为正面,第二个抛硬币的结果为正面,第三个抛硬币的结果为反面,依此类推。我们可以以表格形式显示此关系:

instance of coin tosses

或者,以图形形式:

graphic of coin tosses

验尸报告(我很喜欢这个词“验尸”,听起来很酷)显示抛硬币产生正面和反面的数量。一次运行200次抛硬币得出了此报告:

postmortem report

我认为前面的讨论已经很好地描述了问题的实质:有一个硬币,有一组硬币翻转,每个翻转产生正面或背面,并且有一个验尸报告显示了总数头和尾。这是问题的模型。有很多用于表达模型的语言,但是我特别喜欢的是MIT的Alloy。它不仅是一种建模语言,还是一种工具。使用Alloy,我们不仅可以创建模型,还可以探查模型,研究其特性。我所说的“财产”是什么意思?好吧,这是一个值得研究的特性:一组抛硬币可以产生全部正面(或全部反面)吗?对于Alloy,我们将其表示为断言:

断言:验尸报告将永远不会包含所有正面和反面。

我们可以运行Alloy工具,它将搜索反例。我运行了Alloy,发现了这个反例:

all heads

啊,一组抛硬币 是有可能的! (不太可能,但是可能)

我们还可能要测试模型的正确性:

断言:验尸报告中的正面数目加上反面数目等于抛硬币的数目。

在这种情况下,Alloy找不到反例。那很好。它给我们更大的保证,即模型是正确的(并且我们对问题的理解是正确的)。

好的,现在我觉得我对问题和实现必须具有的属性有了深刻的了解。我准备用Java之类的编程语言来实现它。

。 。 。 。 。 。 。

您中的有些人可能会对Alloy模型感到好奇。我在下面显示。如前所述,我真的很喜欢Alloy。原因之一是其在数学集合论中的基础。数学家研究集合已有数千年的历史,因此Alloy具有坚实的基础。这是抛硬币问题的Alloy模型:

open util/ordering[Coin_Toss]

one sig Coin {
    tosses: Face one -> Coin_Toss
}

sig Coin_Toss {}

enum Face { Heads, Tails }

one sig Report {
    numHeads: Int,
    numTails: Int
}

pred init [c: Coin] {
    Coin.tosses.first = Heads or Coin.tosses.first = Tails
}

pred postmortem [c: Coin] {
    Report.numHeads = #c.tosses[Heads]
    Report.numTails = #c.tosses[Tails]
}

pred coin_tosses {
    init [Coin]
    all t: Coin_Toss - first |
        Coin.tosses.t = Heads or Coin.tosses.t = Tails
    postmortem [Coin]
}

run coin_tosses for 200 but 10 Int

assert Not_all_heads {
    coin_tosses =>
        Report.numTails != 0
}

check Not_all_heads for 200 but 10 Int

assert Num_heads_plus_tails_equals_num_Coin_Toss {
    coin_tosses =>
        add[Report.numHeads, Report.numTails] = #Coin_Toss
}

check Num_heads_plus_tails_equals_num_Coin_Toss for 50 but 7 Int

0 个答案:

没有答案