乘法口诀表

    这次我们要完成一个“极具实用性”的功能 —- 显示乘法口诀表!
    (哦。真的是“太”实用的功能了。)

    这表示我们的人工智能智力水平已经达到小学程度了!(误。)

    除此之外,我们还请到了我另一个人格 —- 括号菌—- 作为我们的评论嘉宾,希望你会喜欢他的风格(没错就是我。)

    首先,让我们仔细思考一下乘法口诀表的真正意义,(这玩意儿还能有啥意义?)
    它其实是两列数字的乘积,就像这样:

    我们只要让它们相乘,这个表就出来了~(嗯,那要怎么让程序去乘呢。)

    首先,我们拿 1 去和 1 相乘,再拿 1 和 2 相乘 ……这样就产生了第一列,然后再拿 2去乘,得到第二列。(那么我们就要用一个循环咯?)

    没错,循环是可以的,但是,我们有一个更为强大的东西,循环什么的,太落后了。(那是?)

    下面有请(可能是)函数世界里最常用的高阶函数:

    下面请看前方记者带回的报道

    1. => (map * [4 2 5] [1 6 3])
    2. (4 12 15)
    1. => (map * [4 2] [1 6 3])
    2. (4 12)
    1. => (map inc [-10 11])
    2. (-9 12)

    好的,感谢前方记者和 map 先生的介绍,我们这就来试试看,怎么使用它来产生乘法口诀的第一列吧!

    1. => (map * [1 1 1 1 1 1 1 1 1] [1 2 3 4 5 6 7 8 9])
    2. (1 2 3 4 5 6 7 8 9)

    (额,这个……有点……虽然是成功了吧……)

    嗯,手动输入的确有点麻烦啊,没关系,我们还有重复某一元素的函数 repeat 和 可以生成数字序列的 range ~
    我们来试试看:

    1. => (repeat 10 3) ;产生 10 3 的序列
    2. (3 3 3 3 3 3 3 3 3 3)
    3. => (repeat 5 "*") ;产生 5 "*" 的序列
    4. ("*" "*" "*" "*" "*")
    5. => (range 10) ;从 0 开始产生不大于 10 的数字序列
    6. (0 1 2 3 4 5 6 7 8 9)
    7. => (range 3 10) ;从 3 开始产生不大于 10 的数字序列
    8. (3 4 5 6 7 8 9)

    (这么方便啊。这下好办了。) 
    好了,下面我们就改进一下刚才生成第一行乘法口诀的表达式吧~

    1. (map * (repeat 10 1) (range 1 10))
    2. => (1 2 3 4 5 6 7 8 9)

    那么第二行怎么生成呢?大家应该能写出来了吧~

    (嗯哪,只需要把重复 10 个 1,改成重复 10 个 2 就行啦~)
    所以,我们把它写成一个函数,这样,我们就可以生成任意的一行了。

    1. (defn multi-table-one-line
    2. [num]
    3. (map * (repeat 10 num) (range 1 10)))

    这样以来,第三行就可以这样写了:

    1. => (multi-table-one-line 3)
    2. (3 6 9 12 15 18 21 24 27)

    (那,怎么来一次性生成呢?1 2 3 4……这样依次输入太麻烦了。对了!我们再来一次 map!)
    哈哈,变聪明了嘛另一个我。(那可不,我可是你。)

    1. => (map multi-table-one-line (range 1 10))
    2. ((1 2 3 4 5 6 7 8 9)
    3. (3 6 9 12 15 18 21 24 27)
    4. (5 10 15 20 25 30 35 40 45)
    5. (6 12 18 24 30 36 42 48 54)
    6. (7 14 21 28 35 42 49 56 63)
    7. (8 16 24 32 40 48 56 64 72)
    8. (9 18 27 36 45 54 63 72 81))

    (这个,和我们平常看到的乘法口诀不一样啊,你看,有一半是重复的可以去掉啊,因为乘法是对称的嘛。)
    嗯,对啊,我们仔细想想,第一次的时候,我们并不需要拿 1 1 1 1 1 1 1 1 1 去乘啊,只需一个 1 就可以了,2 的时候 只需两个 2,3 的时候只需三个 3……所以,我们在写 repeat 的时候,应该这么写:(repeat num num),所以生成每行的函数应该改成:

    1. (defn multi-table-one-line
    2. [num]
    3. (map * (repeat num num) (range 1 10)))

    (对啊!这样,由于 map 以第一组参数的数量为准,后面多余的就不会再乘了。)

    我们来看一下效果:

    1. => (map multi-table-one-line (range 1 10))
    2. ((1)
    3. (2 4)
    4. (3 6 9)
    5. (4 8 12 16)
    6. (5 10 15 20 25)
    7. (6 12 18 24 30 36)
    8. (7 14 21 28 35 42 49)
    9. (8 16 24 32 40 48 56 64)
    10. (9 18 27 36 45 54 63 72 81))

    (不错!成功了!但是……平常的乘法口诀,前面都有一个什么 2*3=6 什么的。)

    我们生成的乘法口诀可是真正可以用的一组数据啊!不比加上什么 2*3=6 什么的花哨玩意儿实用多了!(喂喂喂。这些数据有毛线用啊。)

    现在我们来写一个“乘法口诀专用乘法函数”:

    1. (defn special-use-multi
    2. [x y]
    3. (str x "乘" y "得" (* x y)))

    看看效果:

    现在我们再来改装我们生成每行的函数:

    1. (defn multi-table-one-line
    2. [num]
    3. (map special-use-multi (repeat num num) (range 1 10)))

    看看最后效果:

    1. =>(map multi-table-one-line (range 1 10))
    2. (("1乘1得1")
    3. ("2乘1得2" "2乘2得4")
    4. ("3乘1得3" "3乘2得6" "3乘3得9")
    5. ("5乘1得5" "5乘2得10" "5乘3得15" "5乘4得20" "5乘5得25")
    6. ("6乘1得6" "6乘2得12" "6乘3得18" "6乘4得24" "6乘5得30" "6乘6得36")
    7. ("7乘1得7" "7乘2得14" "7乘3得21" "7乘4得28" "7乘5得35" "7乘6得42" "7乘7得49")
    8. ("8乘1得8" "8乘2得16" "8乘3得24" "8乘4得32" "8乘5得40" "8乘6得48" "8乘7得56" "8乘8得64")
    9. ("9乘1得9" "9乘2得18" "9乘3得27" "9乘4得36" "9乘5得45" "9乘6得54" "9乘7得63" "9乘8得72" "9乘9得81"))

    大功告成啦~可喜可贺~

    好啦,我们下次再见~

    (别别别别啊!你这怎么有这么多括号啊!)

    什么啊,我们这可是一个活的可用的数据啊!比那些死的输入到屏幕上没屁用的东西强一万倍啊!

    唉,真难伺候,好吧,我们可以使用 printprintln 函数来输出我们的二维表,也就是两层嵌套的列表,也就是我们的乘法口诀。

    首先,我们再来介绍一个高阶函数:

    apply (怎么字儿又大了!又要采访了么!)

    由于 apply 先生不在家,我们有机会再来采访他!(那你搞这么隆重干啥!)

    我们现在认为它可以把列表的括号去掉:

    1. => (print [1 2 3])
    2. [1 2 3] nil
    3. => (apply print [1 2 3])
    4. 1 2 3 nil

    好了,我们来完成我们输出二维表的函数:

    1. (defn print-each
    2. "输出每一行" ; 我们可以在函数名和参数之间另起一行,使用字符串作为函数的说明。
    3. [some-coll]
    4. (apply print some-coll)
    5. (println))
    6. (defn print-coll-2d
    7. "输出整体"
    8. [some-coll]
    9. (map print-each some-coll))

    print-coll-2d 不仅能输出乘法口诀了,它可以输出任何一个二维的表。
    下面我们就进行最后的输出工作了。

    1. => (print-coll-2d (map multi-table-one-line (range 1 10)))
    1. 111
    2. 212 224
    3. 313 326 339
    4. 414 428 4312 4416
    5. 515 5210 5315 5420 5525
    6. 616 6212 6318 6424 6530 6636
    7. 717 7214 7321 7428 7535 7642 7749
    8. 818 8216 8324 8432 8540 8648 8756 8864
    9. 919 9218 9327 9436 9545 9654 9763 9872 9981
    10. (nil nil nil nil nil nil nil nil nil)

    这下好了吧~
    (嗯……对齐效果不行,而且最后怎么多了一行。)
    对齐可以使用格式化输出,最后的是 map 函数返回的 print-each 的值,不可以消除返回值但是……你屁事儿咋这么多呢!下次不叫你了!