



























Amy 和 Frank 冲下楼,发现门锁了。窗外,昨天的 Amy 正拿着笔记本电脑朝大门走来。
Frank 翻开《Clojure 搭车客指南》的"锁门及其他小麻烦"一章。指南推荐用 fnil 来解决锁门问题。 fnil 接受一个已有函数,返回一个新函数——当参数为 nil 时,用你指定的默认值代替:
(defn locked-door [key] (if key "open" "nope - staying shut")) (locked-door :key) ;; => "open" (locked-door nil) ;; => "nope - staying shut" (def this-door (fnil locked-door :another-key-that-works)) (this-door :key) ;; => "open" (this-door nil) ;; => "open"
门开了。两人开车来到动物园的水族馆。
水獭,Frank 解释说,是"前线时间守卫"。看过那些水獭用石头砸贝壳的自然视频吗?其实它们在求值 Clojure 表达式--以维持人类文明的正常运行。它们大多数时候喜欢远程工作(仰面漂浮在水面上,效率最高),有时会建造动物园或水族馆来近距离监控特定区域。
Frank 拿出四颗迷你棉花糖。给了 Amy 两颗,自己一颗塞进耳朵,一颗塞进嘴里,开始和水獭对话。
指南说,迷你棉花糖是创建便携式 Clojure core.async 通道的最佳材料——它不会在手里融化。
core.async 是 Clojure 的并发编程模型,灵感来自 Go 语言的 goroutine 和 channel。通道(channel)是 core.async 的核心概念——你可以把它想象成一根管子,一端放东西,另一端取东西。 core.async 有两套操作符: - 两个叹号的 ~>!!~ / ~<!!~ 是阻塞操作: ~>!!~ 往通道放消息(put), ~<!!~ 从通道取消息(take)。两个叹号表示"我会一直等到操作完成",在此期间当前线程被卡住 - 一个叹号的 ~>!~ / ~<!~ 是非阻塞操作:功能一样,但只能在 ~go~ 块内使用。一个叹号表示"我不会阻塞线程", ~go~ 块会在等待时自动挂起当前逻辑、释放线程给别的任务用
用 chan 创建一个通道:
(def talk-to-otters-chan (chan))
默认情况下通道是无缓冲的(unbuffered),也就是棉花糖的原始大小。无缓冲通道要求发送方和接收方同时在场才能通信——就像面对面说话,你说一句,对方得听着,不然你就卡在那。
用阻塞操作 >!! 往通道放消息,会阻塞当前线程:
(>!! talk-to-otters-chan "Hello otters.") ;; 线程会卡住,直到有接收方
所以一个办法是用 future 在另一个线程里发送:
(future (>!! talk-to-otters-chan "Hello otters.")) (<!! talk-to-otters-chan) ;; => "Hello otters."
也可以用带缓冲的通道(相当于把棉花糖撑大一点):
(def talk-to-otters-chan (chan 10)) ;; 缓冲区大小为 10 (>!! talk-to-otters-chan "Hello otters.") ;; => nil(不阻塞,消息放进缓冲区了) (>!! talk-to-otters-chan "Do you know anything about the world ending?") ;; => nil (<!! talk-to-otters-chan) ;; => "Hello otters." (<!! talk-to-otters-chan) ;; => "Do you know anything about the world ending?"
但最好的方式是用 go 块实现异步通信——不阻塞任何线程。在 go 块里用非阻塞操作 >! (一个叹号)和 <! (一个叹号):
(def talk-to-otters-chan (chan))
(go (while true
(println (<! talk-to-otters-chan))))
(>!! talk-to-otters-chan "Hello otters")
(>!! talk-to-otters-chan "Do you know anything about the world ending?")
(>!! talk-to-otters-chan "Also, you are really fuzzy and cute.")
;; REPL 中的输出:
;; Hello otters
;; Do you know anything about the world ending?
;; Also, you are really fuzzy and cute.
更进一步的例子——双向对话。创建两个通道,一个负责说,一个负责听。~go~ 块之间通过通道自动转发消息:
(def talk-chan (chan))
(def listen-chan (chan))
(go (while true
(println (<! listen-chan))))
(go (while true
(>! listen-chan
(str "You said: " (<! talk-chan) " Do you have any Abalone?"))))
(>!! talk-chan "Hello otters")
(>!! talk-chan "Do you know anything about the world ending?")
(>!! talk-chan "Also, you are really fuzzy and cute.")
;; REPL 中的输出:
;; You said: Hello otters Do you have any Abalone?
;; You said: Do you know anything about the world ending? Do you have any Abalone?
;; You said: Also, you are really fuzzy and cute. Do you have any Abalone?
水獭每次都问"你有鲍鱼吗?"——鲍鱼壳是水獭用来砸东西的工具,也是它们的薪水。
Amy 把一颗棉花糖塞进耳朵,立刻听到了 Frank 和水獭的对话。她伸手要把另一颗塞进嘴里问一个重要问题——但不小心碰到了求值器上那个大红 Source 按钮。她和 Frank 被一个漩涡卷起来,吸入地下。
故事戛然而止。Carin Meier 没有写第四篇。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。