Clojure核心匹配错误与变体

时间:2015-07-28 14:57:40

标签: clojure clojure-core.typed clojure-core.match

我正在尝试使用带有核心/匹配和核心/类型的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],其中权重是一个数字)或者它是一个移动(它的形式为[:mobile]移动]移动是移动类型的东西。)

它进行类型检查,总权重函数适用于简单的输入:由两个权重组成的移动设备。

但正如您所看到的那样,对于具有移动分支的移动设备而言,它失败了。

由于某种原因,它与[:mobile mobile]案例不匹配。我知道自己做错了什么?

1 个答案:

答案 0 :(得分:1)

问题似乎是

中的标识符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))))

...删除错误。

规则似乎是:

  

如果绑定了match模式中的名称,则不会在本地反弹。它   被解释为它的主导价值。

我不得不说我只是偶然发现了这个错误。我希望本地绑定优先,但事实并非如此。

备注

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)))