Clojure 核心匹配错误与变体
Clojure core match error with variant
我正在尝试使用带有 core/match 和 core/typed 的 clojure 解决 this SICP 练习。
我有点关注 Jeanine Adkisson 的 "Variants are Not Unions" 演讲。
这是我目前得到的:
(ns sicp.chapter2_ex29
(:require [clojure.core.typed :as t]
[clojure.core.match :refer [match]]))
; Type definitions
(t/defalias Mobile '{:left Branch, :right Branch})
(t/defalias Weight Number)
(t/defalias Structure
(t/U '[(t/Value :weight) Weight]
'[(t/Value :mobile) Mobile]))
(t/defalias Branch '{:length Number, :structure Structure})
; Constructors
(t/ann make-mobile [Branch Branch -> Mobile])
(defn make-mobile [left right]
{:left left :right right})
(t/ann make-branch [Number Structure -> Branch])
(defn make-branch [length structure]
{:length length :structure structure})
; Getters
(t/ann left-branch [Mobile -> Branch])
(defn left-branch [mobile]
(:left mobile))
(t/ann right-branch [Mobile -> Branch])
(defn right-branch [mobile]
(:right mobile))
(t/ann branch-length [Branch -> Number])
(defn branch-length [branch]
(:length branch))
(t/ann branch-structure [Branch -> Structure])
(defn branch-structure [branch]
(:structure branch))
; Total weight
(t/ann total-weight [Mobile -> Number])
(defn total-weight [mobile]
(t/letfn> [structure-weight :- [Structure -> Number]
(structure-weight [structure]
(do
(println (str "structure -> " structure))
(println (str "structure0 -> " (get structure 0)))
(println (str "structure1 -> " (get structure 1)))
(match structure
[:weight weight] weight
[:mobile mobile] (total-weight mobile))))
branch-weight :- [Branch -> Number]
(branch-weight [branch]
(structure-weight (branch-structure branch)))]
(let
[left (branch-weight (left-branch mobile))
right (branch-weight (right-branch mobile))]
(do
(println (str "left ->" left))
(println (str "right ->" right))
(+ left right)))))
(t/ann mobile1 Mobile)
(def mobile1 (make-mobile
(make-branch 3 [:weight 4])
(make-branch 5 [:weight 2])))
(total-weight mobile1) ; <- works as expected = 6
(t/ann mobile2 Mobile)
(def mobile2 (make-mobile
(make-branch 3 [:weight 4])
(make-branch 5 [:mobile (make-mobile
(make-branch 2 [:weight 3])
(make-branch 4 [:weight 2]))])))
(total-weight mobile2) ; <- throws java.lang.IllegalArgumentException: No matching clause: [:mobile {:left {:length 2, :structure [:weight 3]}, :right {:length 4, :structure [:weight 2]}}]
结构类型是一个变体:它要么是一个权重,它只是一个数字(它具有 [:weight weight] 的形式,其中 weight 是一个数字)或者它是一个移动(它具有 [:weight weight] 的形式:mobile mobile] 其中 mobile 是 Mobile 类型的东西)。
它会进行类型检查,总权重函数适用于一个简单的输入:一个由两个权重组成的移动设备。
但是如您所见,对于具有移动分支的移动设备,它会失败。
出于某种原因,它与 [:mobile mobile] 案例不匹配。知道我做错了什么吗?
问题似乎是
中的标识符 mobile
(match structure
[:weight weight] weight
[:mobile mobile] (total-weight mobile))))
... 已作为参数绑定在封闭函数定义中:
(defn total-weight [mobile]
... )
将 match
形式更改为
(match structure
[:weight w] w
[:mobile m] (total-weight m))))
... 删除错误。
规则似乎是:
If a name in a match
pattern is bound, it is not rebound locally. It
is interpreted as its prevailing value.
我不得不说我只是偶然发现了这个错误。我希望本地绑定优先,但事实并非如此。
注释
您的 match
表达式的语法不符合 the documentation,这需要 ...
(match [structure]
[[:weight w]] w
[[:mobile m]] (total-weight m))
...但是您的缩写版本也能正常工作。
Clojure 的习惯用法是尽可能使用标准数据结构(尤其是映射),避免访问函数和构造函数。这样,我们可以这样写
; Total weight
(declare structure-weight)
(t/ann total-weight [Mobile -> Number])
(defn total-weight [mobile]
(match [mobile]
[{:left bl, :right br}]
(apply + (map
(comp structure-weight :structure)
[bl br]))))
(t/ann structure-weight [Structure -> Number])
(defn structure-weight [structure]
(match [structure]
[[:weight w]] w
[[:mobile m]] (total-weight m)))
我正在尝试使用带有 core/match 和 core/typed 的 clojure 解决 this SICP 练习。
我有点关注 Jeanine Adkisson 的 "Variants are Not Unions" 演讲。
这是我目前得到的:
(ns sicp.chapter2_ex29
(:require [clojure.core.typed :as t]
[clojure.core.match :refer [match]]))
; Type definitions
(t/defalias Mobile '{:left Branch, :right Branch})
(t/defalias Weight Number)
(t/defalias Structure
(t/U '[(t/Value :weight) Weight]
'[(t/Value :mobile) Mobile]))
(t/defalias Branch '{:length Number, :structure Structure})
; Constructors
(t/ann make-mobile [Branch Branch -> Mobile])
(defn make-mobile [left right]
{:left left :right right})
(t/ann make-branch [Number Structure -> Branch])
(defn make-branch [length structure]
{:length length :structure structure})
; Getters
(t/ann left-branch [Mobile -> Branch])
(defn left-branch [mobile]
(:left mobile))
(t/ann right-branch [Mobile -> Branch])
(defn right-branch [mobile]
(:right mobile))
(t/ann branch-length [Branch -> Number])
(defn branch-length [branch]
(:length branch))
(t/ann branch-structure [Branch -> Structure])
(defn branch-structure [branch]
(:structure branch))
; Total weight
(t/ann total-weight [Mobile -> Number])
(defn total-weight [mobile]
(t/letfn> [structure-weight :- [Structure -> Number]
(structure-weight [structure]
(do
(println (str "structure -> " structure))
(println (str "structure0 -> " (get structure 0)))
(println (str "structure1 -> " (get structure 1)))
(match structure
[:weight weight] weight
[:mobile mobile] (total-weight mobile))))
branch-weight :- [Branch -> Number]
(branch-weight [branch]
(structure-weight (branch-structure branch)))]
(let
[left (branch-weight (left-branch mobile))
right (branch-weight (right-branch mobile))]
(do
(println (str "left ->" left))
(println (str "right ->" right))
(+ left right)))))
(t/ann mobile1 Mobile)
(def mobile1 (make-mobile
(make-branch 3 [:weight 4])
(make-branch 5 [:weight 2])))
(total-weight mobile1) ; <- works as expected = 6
(t/ann mobile2 Mobile)
(def mobile2 (make-mobile
(make-branch 3 [:weight 4])
(make-branch 5 [:mobile (make-mobile
(make-branch 2 [:weight 3])
(make-branch 4 [:weight 2]))])))
(total-weight mobile2) ; <- throws java.lang.IllegalArgumentException: No matching clause: [:mobile {:left {:length 2, :structure [:weight 3]}, :right {:length 4, :structure [:weight 2]}}]
结构类型是一个变体:它要么是一个权重,它只是一个数字(它具有 [:weight weight] 的形式,其中 weight 是一个数字)或者它是一个移动(它具有 [:weight weight] 的形式:mobile mobile] 其中 mobile 是 Mobile 类型的东西)。
它会进行类型检查,总权重函数适用于一个简单的输入:一个由两个权重组成的移动设备。
但是如您所见,对于具有移动分支的移动设备,它会失败。
出于某种原因,它与 [:mobile mobile] 案例不匹配。知道我做错了什么吗?
问题似乎是
中的标识符mobile
(match structure
[:weight weight] weight
[:mobile mobile] (total-weight mobile))))
... 已作为参数绑定在封闭函数定义中:
(defn total-weight [mobile]
... )
将 match
形式更改为
(match structure
[:weight w] w
[:mobile m] (total-weight m))))
... 删除错误。
规则似乎是:
If a name in a
match
pattern is bound, it is not rebound locally. It is interpreted as its prevailing value.
我不得不说我只是偶然发现了这个错误。我希望本地绑定优先,但事实并非如此。
注释
您的 match
表达式的语法不符合 the documentation,这需要 ...
(match [structure]
[[:weight w]] w
[[:mobile m]] (total-weight m))
...但是您的缩写版本也能正常工作。
Clojure 的习惯用法是尽可能使用标准数据结构(尤其是映射),避免访问函数和构造函数。这样,我们可以这样写
; Total weight
(declare structure-weight)
(t/ann total-weight [Mobile -> Number])
(defn total-weight [mobile]
(match [mobile]
[{:left bl, :right br}]
(apply + (map
(comp structure-weight :structure)
[bl br]))))
(t/ann structure-weight [Structure -> Number])
(defn structure-weight [structure]
(match [structure]
[[:weight w]] w
[[:mobile m]] (total-weight m)))