我正在学习Jason Hickey's Introduction to Objective Caml。
有这样的练习:
练习4.3假设我们有一个基于以下替换密码的加密系统,其中每个普通字母都根据下表加密。
Plain | A B C D -------------------- Encrypted | C A D B
例如,字符串
BAD
将加密为ACB
。写一个函数
check
,给定一个明文字符串 s 1 和一个密文字符串 s 2 < / sub>,如果且仅当 s 2 是 s 1的密文时返回true
子>。如果 s 1 不是明文字符串,则您的函数应引发异常。您可能希望参考第8页的字符串操作。当字母变大时,您的代码如何缩放? [强调添加]
基本上,我用might-be-stupid-naive
方式为这个练习编写了两个函数。
我想首先就我的解决方案寻求建议。
然后我想询问练习中突出显示的缩放解决方案的提示。
let check_cipher_1 s1 s2 =
let len1 = String.length s1 in
let len2 = String.length s2 in
if len1 = len2 then
let rec check pos =
if pos = -1 then
true
else
let sub1 = s1.[pos] in
let sub2 = s2.[pos] in
match sub1 with
| 'A' -> (match sub2 with
|'C' -> check (pos-1)
| _ -> false)
| 'B' -> (match sub2 with
|'A' -> check (pos-1)
| _ -> false)
| 'C' -> (match sub2 with
|'D' -> check (pos-1)
| _ -> false)
| 'D' -> (match sub2 with
|'B' -> check (pos-1)
| _ -> false)
| _ -> false;
in
check (len1-1)
else
false
let check_cipher_2 s1 s2 =
let len1 = String.length s1 in
let len2 = String.length s2 in
match () with
| () when len1 = len2 ->
let rec check pos =
match pos with
| -1 -> true
| _ ->
let sub1 = s1.[pos] in
let sub2 = s2.[pos] in
(*http://stackoverflow.com/questions/257605/ocaml-match-expression-inside-another-one*)
match sub1 with
| 'A' -> (match sub2 with
|'C' -> check (pos-1)
| _ -> false)
| 'B' -> (match sub2 with
|'A' -> check (pos-1)
| _ -> false)
| 'C' -> (match sub2 with
|'D' -> check (pos-1)
| _ -> false)
| 'D' -> (match sub2 with
|'B' -> check (pos-1)
| _ -> false)
| _ -> false
in
check (len1-1)
| () -> false
确定。以上两种解决方案类似。
我制作了这两个,因为在这里http://www.quora.com/OCaml/What-is-the-syntax-for-nested-IF-statements-in-OCaml,有些人说if else
不是首选。
这基本上是我一生中第一次写not-that-simple
函数。所以我真的很想提出建议。
例如,
match
而不是if else
吗?rec
或use the rec
?in check (len1-1)
正确吗?练习要求How does your code scale as the alphabet gets larger?
。我现在真的没有线索。在Java中,我会说我会有一个map
,然后对于s1
中的每个字符,我正在寻找s2
的相关字符并查看它是否是地图中的值
有关于此的任何建议吗?
答案 0 :(得分:5)
这是一个简单的解决方案:
let tr = function | 'A' -> 'C' | 'B' -> 'A' | 'C' -> 'D' | 'D' -> 'B' | _ -> failwith "not a plaintext" let check ~tr s1 s2 = (String.map tr s1) = s2 check ~tr "BAD" "ACD"
你可以通过与tr合成来添加更多的字母。即。
let comp c1 c2 x = try (c1 x) with _ -> (c2 x) let tr2 = comp tr (function | 'X' -> 'Y')
答案 1 :(得分:3)
- 我该如何改进这些解决方案?
你滥用缩进使得程序更难阅读。消除不必要的标签并将check
移到外部范围以便于阅读:
let check_cipher_1 s1 s2 =
let rec check pos =
if pos = -1 then
true
else
let sub1 = s1.[pos] in
let sub2 = s2.[pos] in
match sub1 with
| 'A' -> (match sub2 with
|'C' -> check (pos-1)
| _ -> false)
| 'B' -> (match sub2 with
|'A' -> check (pos-1)
| _ -> false)
| 'C' -> (match sub2 with
|'D' -> check (pos-1)
| _ -> false)
| 'D' -> (match sub2 with
|'B' -> check (pos-1)
| _ -> false)
| _ -> false in
let len1 = String.length s1 in
let len2 = String.length s2 in
if len1 = len2 then
check (len1-1)
else false
- 我应该更喜欢匹配吗?
取决于具体情况。如果您在第二个函数(match () with | () when len1 = len2
)中演示模式匹配是肤浅的,那么与简单的if/else
构造相比,它会带来 no 值。如果您对值进行模式匹配,则优于if/else
,并且在使用高级构造时可能会更短。例如,您可以通过匹配元组来缩短函数:
let check_cipher_1 s1 s2 =
let rec check pos =
if pos = -1 then
true
else
match s1.[pos], s2.[pos] with
| 'A', 'C' | 'B', 'A'
| 'C', 'D' | 'D', 'B' -> check (pos-1)
| _ -> false in
let len1 = String.length s1 in
let len2 = String.length s2 in
len1 = len2 && check (len1 - 1)
在这里,我们还使用Or模式对具有相同输出操作的模式进行分组,并用if/else
替换不必要的&&
块。
- 我是在设计rec还是正确使用rec?
- 如果检查(len1-1)正确吗?
你的功能很好看。没有比在OCaml顶层使用一些输入进行测试更好的方法了。
缩放
模式的数量随着字母的大小线性增长。这是非常好的IMO。
答案 2 :(得分:2)
最简单的解决方案似乎只是加密文本并比较结果:
let cipher_char = function
| 'A' -> 'C'
| 'B' -> 'A'
| 'C' -> 'D'
| 'D' -> 'B'
| _ -> failwith "cipher_char"
let cipher = String.map cipher_char
let check_cipher s1 s2 = (cipher s1 = s2)
cipher_char
函数与字母表的大小成线性比例。为了使它更紧凑和通用,你可以使用某种形式的查找表,例如
(* Assume that only letters are needed *)
let cipher_mapping = "CADB"
let cipher_char c =
try cipher_mapping.[Char.code c - Char.code 'A']
with Invalid_argument _ -> failwith "cipher_char"