99种用Racket说I love you的方式

来源 https://www.tuicool.com/articles/qiyeAz

原文  http://www.soimort.org/posts/145/

今天是3月14日,也就是传说中的 白色情人节 (据说是个被表白一方向表白方回赠礼物以表示心意的好日子,蕴含着人们对天下有情人终成眷属的 良好祝愿 )。

作为一个技术宅,应该在这一天准备什么样的礼物给妹子捏?99朵玫瑰?还是巧克力?可不要被这些 商家营销手段 给骗了——就像Sheldon家Amy说的一样,Nerds们过一个真正属于自己的“No dinner, no romance, no gifts”的情人节才是正道啊;这种时候,只要像往常那样写写代码,用代码来说出“I love you”就好了。当然,为了表示诚意,我们要用99种——没错,是用99种不同的方式来写同一句话。

用什么语言写好捏?当然是 满满的都是爱 的 Racket 咯ヽ(♡´ω`♡)ノ

(先放上一段忘了从哪偷来的代码……在DrRacket里运行,就能看到一颗doki~doki跳动的红心哦)

#lang racket

(require 2htdp/universe 2htdp/image)

(define scale-factor 10)
(define window-size (* scale-factor 20)) (define (lobe shape size) (shape (* size scale-factor) 'solid 'red))
(define • (lobe circle 4))
(define •• (overlay/offset • (* 7 scale-factor) 0 •))
(define ▼ (rotate 180 (lobe triangle 14)))
(define ♥ (overlay/offset •• 0 (* 8 scale-factor) ▼)) (define amplitude 1/10)
(define speed 1/2) (define (☺♥ n)
(overlay (scale (add1 (* (sin (* n speed)) amplitude)) ♥)
(empty-scene window-size window-size)))
(animate ☺♥)

下面开始,进入正题。

以前,穷屌丝书生喜欢发问,茴香豆的茴字有几种写法?而高富帅则会问,I love you里面的love有几种make法?不管做任何一件事情,方法上的选择总是很重要——对于敲代码的程序猿来说也一样。

在Python里,做任何事,通常有且仅有一种正确的方法;Pythonic教的传教士说:只有一种姿势最优雅,其他的体位都是邪恶的。但是Rubyists们却不以为然。他们是一群奉行自由主义的嬉皮,不需要Python里面那种类似 哲♂学 一样的教条束缚;在解决同一个最基本的问题上有时也会跟随feeling写出新奇而孑然不同的代码。

记得刚开始接触Ruby那会,试着 用Ruby解过 那套经典的 P-99(99个Prolog问题) 。前面好几十个问题差不多都是关于list的操作:做过一遍之后立刻就感觉到,Ruby单从表达问题的灵活性上来说,的确不是现有的其他主流语言所能够比拟的——显然,作为Perl的继任者,Ruby很好地发扬光大了这一点。

嘛,当然,要说到表达能力,基于 抖S前入式 、把数据和代码结构高度统一起来的Lisp才是编程语言中真正的总攻(虽然这也让一部分Lisper养成了光说空话不干实事+吹嘘自己造车轮造得多么的好+不合作不贡献社区的 坏毛病 )。话说回来,在Lisp里,哪怕是做最简单的一件事情——构造一个list,也能凭空造出千般万般变化。不信?继续看下去。

为了帮助童鞋们更好地理解Racket语言中的核心概念,Univ of Utah的CS系教授Matthew Might写了这篇 《99 ways to say '(I love you) 》。你不仅能够从中领略到Lisp那无与伦比的表达能力,还能借此复习一下Scheme/Racket中的那些基础知识点:lambdas,higher-order function,pattern matching,delayed evaluation,macros,等等——当然,后者才是此文的主要目的。

下面奉上原文翻译。(其实文字部分也就几句话)

Original Article: 99 ways to say '(I love you) by Matthew Might 
(Chinese Translation by Mort Yao )

99 ways to say '(I love you)

虽说其具有本质上的简单性,有关list的那些事儿仍然会常常让Racket程序员们困惑不已。

除开list结构本身之外,Racket程序中各种运算的表达方式更需要花上不少时间来发掘和掌握。

为了更好地强调这些重点,我写了99个不超过一条推长度的Racket表达式,它们求值的结果均得到同样的一个list: '(I love you) 。

这些表达式中大约有一半用来展示构建或操作list的方法,剩下来的则展示了Racket中表述计算的各色方式。

所涉及到的关键知识点包括:

  • 用cons连接单元(cons cells)
  • 字面list记号(literal list notation)
  • 加点list记号(dotted list notation)
  • 反引号表示list(quasiquoted lists)
  • let绑定形式(let-binding forms)
  • vector和stream(vectors and streams)
  • 匿名函数(anonymous functions)
  • 高阶list操作(higher-order list operations)
  • 条件式(conditionals)
  • 结构化模式匹配(structural pattern-matching)
  • 哈希表(hash maps)
  • 续延(continuations)
  • 异常(exceptions)
  • 诺言(promises)
  • 变更(mutation)
  • 宏(macros)
  • 端口(ports)
  • 未来(futures)
  • 参数(parameters)

以下是具体的99种方式。

如果读到任何一条不能理解的话,不妨在DrRacket的REPL里面尝试摸索一番,直到你彻底弄懂它。

99种方式

下面的每一枚Racket表达式,求值的结果均得到一个list '(I love you) :

(原文里面没有给这99条归类,小标题是翻译君擅自加的,看的时候请注意鉴别……)

1. 构造list的基本手段: quote 和 list

;;;
'(I love you)
;;;
(quote (I love you))
;;;
(list 'I 'love 'you)
;;;
(list (quote I) (quote love) (quote you))

2. 用 cons 连接单元

;;;
(cons 'I (cons 'love (cons 'you '())))
;;;
(cons 'I (cons 'love (list 'you)))
;;;
(cons 'I (list 'love 'you))
;;;
(cons 'I '(love you))

3. 加点list记号(Dotted list notation)

;;;
'(I love . (you))
;;;
'(I . (love . (you)))
;;;
'(I . (love . (you . ())))
;;;
'(I . (love you))

4. 反引号表示list(Quasiquoted lists)

;;;
`(I love you)
;;;
`(I ,'love you)
;;
(quasiquote (I ,'love you))
;;
(quasiquote (I (unquote 'love) you))
;;;
`(I ,`love you)
;;;
(let ([verb 'love])
`(I ,verb you))
;;;
`(I ,(string->symbol "love") you)
;;;
`(I ,(string->symbol (list->string '(#\l #\o #\v #\e))) you)
;;;
`(I love . (you))
;;;
`(I love . ,(list 'you))
;;;
`(I love ,@'(you))
;;;
`(I love (unquote-splicing '(you)))
;;;
`(I ,@(list 'love 'you))
;;;
`(,@(list 'I 'love) you)
;;;
`(,@'(I love you))
;;;
`,'(I love you)
;;;
`(I love you . ,'())

5. car 和 cdr

;;;
(car (list '(I love you)))
;;;
(cdr '(Hark! I love you))

6. let 绑定形式( let -binding forms)

;;;
(let ([words '(love you I)])
(list (car (cdr (cdr words)))
(car words)
(car (cdr words))))
;;;
(let ([words '(love you I)])
(list (caddr words)
(car words)
(cadr words)))
;;;
(let* ([c '(you)]
[b (cons 'love c)]
[a (cons 'I b)])
a)
;;;
(let ()
'(I love you not)
'(I love you))

7. vector

;;;
(vector->list (vector 'I 'love 'you))
;;;
(vector->list #(I love you))

8. stream

;;;
(stream->list (stream 'I 'love 'you))

9. 匿名函数(Anonymous functions)

;;;
((lambda args args) 'I 'love 'you)
;;;
((lambda (one two . rest) rest) 'You 'believe 'I 'love 'you)
;;;
((lambda (a c b) (list a b c)) 'I 'you 'love)
;;;
(apply (lambda (a c b) (list a b c))
(list 'I 'you 'love))
;;;
((lambda (a b [c 'you]) (list a b c)) 'I 'love)
;;;
((lambda (#:foo b #:bar c #:baz a)
(list a b c))
#:baz 'I #:bar 'you #:foo 'love)
;;;
((lambda (a b #:key [c 'me]) (list a b c)) #:key 'you 'I 'love)

10. 柯里化(Currying)

;;;
(let ([f (λ (x)
(λ (y)
(λ (z)
(list x y z))))])
(((f 'I) 'love) 'you))

11. case-lambda

;;;
(let ([f (case-lambda
[() 'I]
[(x) 'love]
[(x y) 'you])])
(list (f) (f 1) (f 1 2)))

12. 基础list操作

;;;
(append '(I love) '(you))
;;;
(append '(I) '(love) '(you))
;;;
(flatten '((I) (love you)))
;;;
(flatten '((I) (love) (you) ()))
;;;
(reverse '(you love I))
;;;
(remove 'cannot '(I cannot love you))
;;;
(remove-duplicates '(I love love love you))
;;;
(take '(I love you not) 3)
;;
(take-right '(I think I love you) 3)
;;;
(drop '(She knows I love you) 2)
;;;
(drop-right '(I love you no more) 2)

13. 高阶list操作(map、filter和reduce)

;;;
(map (lambda (x) (if (eq? x 'hate) 'love x))
'(I hate you))
;;;
(map (λ (i) (vector-ref #(love you I) i))
'(2 0 1))
;;;
(map (λ (k) (hash-ref #hash(("foo" . I)
("baz" . you)
("bar" . love)) k))
'("foo" "bar" "baz"))
;;;
(map string->symbol (sort (list "love" "you" "I") string<?))
;;;
(map string->symbol (string-split "I-love-you" "-"))
;;;
(flatten (map (λ (a b) (cons a b))
'(I love you)
'(() () ())))
;;;
(filter (lambda (x) (not (eq? x 'cannot)))
'(I cannot love you))
;;;
(foldr cons '() '(I love you))
;;;
(foldl cons '() '(you love I))

14. 迭代(Iterations)

;;;
(for/list ([word #(I love you)])
word)

15. 条件式(Conditionals)

;;;
(cond
[(even? 3) '(Not me)]
[(odd? 3) '(I love you)])
;;;
(cond
[(even? 3) '(Not me)]
[(odd? 2) '(Nor I)]
[else '(I love you)])
;;;
(case 1
[(a b c) '(Not me)]
[(3 2 1) '(I love you)])

16. 结构化模式匹配(Structural pattern-matching)

;;;
(match #t
[#f '(Not me)]
[#t '(I love you)])
;;;
(match #t
[#f '(Not me)]
[_ '(I love you)])
;;;
(match 'you
['me '(Not me)]
[x `(I love ,x)])
;;;
(match '(foo bar)
['(foo bar) '(I love you)])
;;;
(match '(I cannot lift you)
[(list 'I 'cannot _ c) `(I love ,c)])
;;;
(match '(2 3 1)
[(list-no-order 3 1 2)
'(I love you)])
;;;
(match '(love you I)
[(list-no-order 'I 'love foo)
`(I love ,foo)])
;;;
(match '(3 . 4)
[(cons 3 4)
'(I love you)])
;;;
(match '(3 love 1)
[(cons 3 (cons x (cons 1 '())))
`(I ,x you)])
;;;
(match '(3 love 1)
[(cons 3 (cons x (cons 1 '())))
`(I (unquote x) you)])
;;;
(match 3
[(? symbol?) '(Not me)]
[(? string?) '(Me neither)]
[(? number?) '(I love you)])
;;;
(match 3
[(not 4) '(I love you)]
[3 'unreachable])
;;;
(match '(you love I)
[`(,c love ,a)
`(,a love ,c)])
;;;
(match '(We love you)
[`(,_ . ,rest)
`(I . ,rest)])
;;;
(match '(We love you)
[`(,_ ,rest ...)
`(I ,@rest)])
;;;
(match '(We love you)
[(list _ rest ...)
`(I ,@rest)])
;;;
(match #(1 love 3)
[(vector (? number?) b (? number?))
`(I ,b you)])
;;;
(match #hash((1 . I) (3 . you) (5 . love))
[(hash-table (1 a) (5 b) (3 c))
(list a b c)])
;;;
(match 'you
[(and x (? symbol?)) `(I love ,x)])
;;;
(match '100
[(app (λ (n) (- n 1)) 99)
'(I love you)])

17. 续延(Continuation)

;;;
(list 'I
(call/cc (λ (cc)
(error (cc 'love))))
'you)

18. 异常(Exceptions)

;;;
(with-handlers ([symbol? (lambda (p)
`(I ,p you))])
(raise 'love))

19. 延迟求值(Delayed evaluation)

;;;
(let ([problem (delay (car '()))])
'(I love you))
;;;
`(I ,(force (delay 'love)) you)
;;;
(letrec ([x (delay (list a b c))]
[a 'I]
[c 'you]
[b 'love])
(force x))

20. 变更(Mutation)

;;;
(let ([word 'know])
(set! word 'love)
`(I ,word you))
;;;
(let ([word-box (box 'know)])
(set-box! word-box 'love)
`(I ,(unbox word-box) you))

21. 宏(Macros)

;;;
(let-syntax ([un-yoda-list
(syntax-rules ()
[(_ c a b) (list 'a 'b 'c)])])
(un-yoda-list you I love))

22. 端口(Ports)

;;;
(let ((in (open-input-string "I love you")))
(cons (read in)
(cons (read in)
(cons (read in) '()))))

23. 未来(Futures)

;;;
(list (touch (future (λ () 'I))) 'love 'you)

24. 参数化(Parameterization)

;;;
(let ([a (make-parameter "a")]
[b (make-parameter "b")]
[c (make-parameter "c")])
(parameterize ([a 'i] [b 'love] [c 'you])
(list (a)
((parameterize ([b 'dislike])
(λ () (b))))
(c))))

来自其他人的贡献

由 David Van Horn 提供:

(define `λ (λ 'love 'I 'you))
((λ λ λ)
`(λ (⊙ λ ∪) λ)
`(λ (λ ⊙ ∪) λ)
`(λ (∪ ⊙ λ) λ))

由Bruce Bolick提供:

(define (stream-take s n)
(if (= n 0)
'()
(cons (stream-first s)
(stream-take (stream-rest s) (- n 1)))))
(define U 3)
(define I stream-take)
(define ♥ (stream 'I 'love 'you))
(I ♥ U)

(原文完)

(我表示诸位看官到这里可以直接无视以下内容了……看可以,请一笑了之。)

因为文章里提到的99种方式都没有涉及到logic programming,我觉得很有必要写一个自己的版本。用谓词逻辑来推导出“I love you”这种设定会很有趣的不是么……

于是就有了下面这段蛋疼的产物(用到了 Racklog ,一个仿Prolog的逻辑式编程扩展),最终的结果也是一样一样的 '(I love you) :

我要做一只合格的脑残偶像厨WOTA \(^o^)/

#lang racket
(require racklog) (define %love
(%rel ()
[('I 'AKB48)]
[('I 'you)]
[('you 'I)])) (define first-love
(%which (who)
(%and (%is who 'AKB48)
(%love 'I who)
(%love who 'I)))) (define not-that-much-but-still-love
(%which (who)
(%and (%love 'I who)
(%love who 'I)))) (define (♡˙︶˙♡)
`(I love, (cdr (car not-that-much-but-still-love)))) (♡˙︶˙♡)

====================== End

99种用Racket说I love you的方式的更多相关文章

  1. Python 高级特性介绍 - 迭代的99种姿势 与协程

    Python 高级特性介绍 - 迭代的99种姿势 与协程 引言 写这个笔记记录一下一点点收获 测试环境版本: Python 3.7.4 (default, Sep 28 2019, 16:39:19) ...

  2. JavaScript是如何工作的:事件循环和异步编程的崛起 + 5种使用 async/await 更好地编码方式!

    摘要: 深度理解JS事件循环!!! 原文:JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式! 作者:前端小智 Fundebug经授权转载, ...

  3. Javascript学习笔记:2种其他类型转换为数字Number类型的方式

    ①使用parseInt()/parseFloat()(在ECMAScript6中是Number.parseInt()/Number.parseFloat()) console.log(parseInt ...

  4. 一种更高查询性能的列存储方式MaxMinT 第一部分

    简介本文描述了一种列存储方式和对应的查询方法,这种存储方式具有更好的查询性能和更小的存储空间. And查询 本文先用直观的图形方式展示and查询时的方式,这也是算法要解决的问题核心.通常在OLAP数据 ...

  5. 两种利用GCD实现分步获取结果的方式和SDWebImage缓存机制的验证

    前段时间写界面,因为数据的请求分成了两部分,所以用到了多线程,实现数据的分步请求,然后自己写了一个Demo,用两种方式实现分步获取内容,其中也包含了验证SDWebImage这个库的缓存机制,在这里给大 ...

  6. python-几种快速了解函数及模块功能的方式

    背景 在进行编程的时候经常要导入各种包的各种函数,但是很多包一下又不知道为什么要导入这个模块,所以想总结下有哪些方法可以让我们快速熟悉其中函数的作用. import numpy as np impor ...

  7. JavaScript是如何工作的:事件循环和异步编程的崛起+ 5种使用 async/await 更好地编码方式!

    为什么单线程是一个限制? 在发布的第一篇文章中,思考了这样一个问题:当调用堆栈中有函数调用需要花费大量时间来处理时会发生什么? 例如,假设在浏览器中运行一个复杂的图像转换算法. 当调用堆栈有函数要执行 ...

  8. MongoDB 几种查询嵌套数据(Embedded)的方式(转载)

    前言 MongoDB 推荐使用「内嵌文档(Embedded)」,所以带来一个问题,如何查询嵌入文档内的数据? 假如我们有一个 storage 的 Collection,包含一条数据: // `stor ...

  9. CSS3中三种清除浮动(float)影响的方式

    float是HTML中布局的一大关键,很多难题一旦用上float都能很愉快地解决.但是凡是好用的,也容易出错.比如当子元素都为float时,其父元素会受影响,或者偶尔会发现自己某个div的高度变成了0 ...

随机推荐

  1. 快速在Ubuntu安装PHP网站

    快速安装使用的是tasksel,tasksel是Debian / Ubuntu的工具,安装多个相关软件包作为一个协调的“任务”到你的系统.这个lamp-server^跟taskel这个程序有关.下面是 ...

  2. Microsoft Tech Summit 2017

    Microsoft Tech Summit 2017 时间:2017.10.31-2017.11.3 地点:北京国际饭店会议中心

  3. 2015306 白皎 《网络攻防》EXP6 信息搜集与漏洞扫描

    2015306 白皎 <网络攻防>EXP6 信息搜集与漏洞扫描 一.问题回答 (1)哪些组织负责DNS,IP的管理. 顶级的管理者是Internet Corporation for Ass ...

  4. JavaScript快速入门-ECMAScript本地对象(Date)

    JavaScript中的Date 对象用于处理日期和时间. var myDate=new Date()  #Date 对象会自动把当前日期和时间保存为其初始值. 一.Date对象的方法 方法 示例 n ...

  5. DokuWiki 使用

    新建文件夹 修改url, 将新文件夹的名称赋值给url上的id, 如要建一个"DokuWiki"的文件夹,并在文件夹下新增一个"QuickStart"的页面,改 ...

  6. python代码实现经典排序算法

    排序算法在程序中有至关重要的作用, 不同算法的时间复杂度和空间复杂度都有所区别, 这影响着程序运行的效率和资源占用的情况, 经常对一些算法多加练习, 强化吸收, 可以提高对算法的理解, 进而运用到实践 ...

  7. 关于App自动化执行链接Appium服务包名正确但是报错An unknown server-side error occurred while processing the command

    在执行链接Appium服务时连接失败可能原因: 1.报错截图: 2.先检查包名是否正确(正常情况下包名不会错误)通过命令行查看包名:aapt dump badging xxx.apk 3.检查对应包的 ...

  8. jmeter实战1

  9. NO.3:自学tensorflow之路------MNIST识别,神经网络拓展

    引言 最近自学GRU神经网络,感觉真的不简单.为了能够快速跑完程序,给我的渣渣笔记本(GT650M)也安装了一个GPU版的tensorflow.顺便也更新了版本到了tensorflow-gpu 1.7 ...

  10. js中文汉字按拼音排序

    JavaScript 提供本地化文字排序,比如对中文按照拼音排序,不需要程序显示比较字符串拼音. String.prototype.localeCompare 在不考虑多音字的前提下,基本可以完美实现 ...