Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

更大的例子:破解凯撒编码

(A larger example: cracking Caesar encoding)

这是一个稍大的程序,灵感来自 Graham Hutton 最优秀的“Programming in Haskell”书中的一个示例:

import std/num/float64
// 英语字母频率表
val english = [8.2,1.5,2.8,4.3,12.7,2.2,
               2.0,6.1,7.0,0.2,0.8,4.0,2.4,
               6.7,7.5,1.9,0.1, 6.0,6.3,9.1,
               2.8,1.0,2.4,0.2,2.0,0.1]

// 小辅助函数
fun percent( n : int, m : int )
  100.0 * (n.float64 / m.float64)

fun rotate( xs : list<a>, n : int ) : list<a>
  xs.drop(n) ++ xs.take(n)

// 计算字符串的字母频率表
fun freqs( s : string ) : list<float64>
  val lowers = list('a','z')
  val occurs = lowers.map( fn(c) s.count(c.string) )
  val total  = occurs.sum
  occurs.map( fn(i) percent(i,total) )

// 根据卡方分布统计计算两个频率表的匹配程度
fun chisqr( xs : list<float64>, ys : list<float64> ) : float64
  zipwith(xs,ys, fn(x,y) ((x - y)^2.0)/y ).foldr(0.0,(+))

// 破解凯撒编码的字符串
fun uncaesar( s : string ) : string
  val table  = freqs(s)                   // 为s建一个频率表
  val chitab = list(0,25).map fn(n)       // 为0到25每种移位构建卡方数字列表
                 chisqr( table.rotate(n), english )

  val min    = chitab.minimum()           // 找到最小元素
  // 找到最小卡方值的位置 n,取负得到解码所需的反向移位
  val shift  = chitab.index-of( fn(f) f == min ).negate
  s.encode( shift )

fun test-uncaesar()
  println( uncaesar( "nrnd lv d ixq odqjxdjh" ) )

val 关键字声明一个静态值。在示例中,值 english 是浮点数(float64 类型)列表,表示每个字母的平均频率。函数 freqs 为特定字符串构建频率表,而函数 chisqr 计算两个频率表的匹配程度。在函数crack中,这些函数用于查找一个移位值,该值产生一个频率表与英语频率表最接近的字符串——我们用它来解码该字符串。