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. CUDA2.4-原理之性能优化及浮点运算

    本部分来自于<大规模并行处理器编程实战>第六章.第七章.打算不再看这本书了,准备看<programming massively parallel processors 2nd> ...

  2. AI1.1-人工智能史

    来自:http://zh.wikipedia.org/wiki/人工智能史#CITEREFBerlinski2000 这篇是来自维基百科上面的人工智能史,将其大部分保留(真的是大部分,所以差不多没有原 ...

  3. lwip lwiperf 方法进行性能测试 4.5MB/S

    硬件配置: STM32F407 + DP83848 + FreeRTOS V10.1.1 + LWIP 2.1.2    2018年12月5日14:31:24 1.先读取 PHY 寄存器 , 查看 自 ...

  4. allegro中Autosilk top, Silkscreen top 和Assembly top三个什么区别(转)

    allegro中Autosilk top, Silkscreen top 和Assembly top三个什么区别(转) Autosilk top, Silkscreen top 和Assembly t ...

  5. .NetCore Session.Redis (转载)

    首先创建ASP.NET CORE Web项目,然后按如下顺序操作. 1.添加nuget程序包: Microsoft.AspNetCore.Session; Microsoft.AspNetCore.D ...

  6. 在 ASP.NET CORE 中使用 SESSION (转载)

    Session 是保存用户和 Web 应用的会话状态的一种方法,ASP.NET Core 提供了一个用于管理会话状态的中间件.在本文中我将会简单介绍一下 ASP.NET Core 中的 Session ...

  7. 20155313 杨瀚 《网络对抗技术》实验一 PC平台逆向破解(5)M

    exp1 PC平台逆向破解(5)M 一.实验内容 1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数. 2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发 ...

  8. Java技术——String类为什么是不可变的

    0. 前言   如果一个对象,在它创建完成之后不能再改变它的状态,包括对象内的成员变量.基本数据类型的值等等.那么这个对象就是不可变的.众所周知String类就是不可变的.转载请注明出处为SEU_Ca ...

  9. Selenium-Switch与SelectApi接口详解

    Switch 我们在UI自动化测试时,总会出现新建一个tab页面.弹出一个浏览器级别的弹框或者是出现一个iframe标签,这时我们用WebDriver提供的Api接口就无法处理这些情况了.需要用到Se ...

  10. flask_admin 笔记三 客户化视图

    客户化视图1, model数据模型参数配置1)配置全局参数内置的ModelView类很适合快速入门. 但是,您需要配置其功能以适合您的特定型号. 这是通过设置ModelView类中提供的配置属性的值来 ...