Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macros Clojure range案例宏_Macros_Clojure_Case - Fatal编程技术网

Macros Clojure range案例宏

Macros Clojure range案例宏,macros,clojure,case,Macros,Clojure,Case,在R.Kent Dybvig的书中,作者在第86页为接受条件范围的case语句编写了定义语法(Scheme宏)。我想在Clojure试试这个 结果如下 我该如何改进这一点?我对范围运算符使用:ii,:ie,:ei,和:ee,表示包含-包含,包含-排除,包含-排除, 分别为独家和独家。有更好的选择吗 我选择扩展到cond语句,而不是离散的if语句,因为我觉得我将从cond宏的任何未来改进中获益 (defmacro range-case [target & cases] "Compare

在R.Kent Dybvig的书中,作者在第86页为接受条件范围的
case
语句编写了
定义语法
(Scheme宏)。我想在Clojure试试这个

结果如下

我该如何改进这一点?我对范围运算符使用
:ii
:ie
:ei
,和
:ee
,表示包含-包含,包含-排除,包含-排除, 分别为独家和独家。有更好的选择吗

我选择扩展到
cond
语句,而不是离散的
if
语句,因为我觉得我将从
cond
宏的任何未来改进中获益

(defmacro range-case [target & cases]
  "Compare the target against a set of ranges or constant values and return
   the first one that matches. If none match, and there exists a case with the
   value :else, return that target. Each range consists of a vector containing
   3 terms: a lower bound, an operator, and an upper bound. The operator must
   be one of :ii, :ie, :ei, or :ee, which indicate that the range comparison
   should be inclusive-inclusive, inclusive-exclusive, exclusive-inclusive,
   or exclusive-exclusive, respectively.
   Example:
     (range-case target
                 [0.0 :ie 1.0] :greatly-disagree
                 [1.0 :ie 2.0] :disagree
                 [2.0 :ie 3.0] :neutral
                 [3.0 :ie 4.0] :agree
                 [4.0 :ii 5.0] :strongly-agree
                 42 :the-answer
                 :else :do-not-care)
   expands to
     (cond
       (and (<= 0.0 target) (< target 1.0)) :greatly-disagree
       (and (<= 1.0 target) (< target 2.0)) :disagree
       (and (<= 2.0 target) (< target 3.0)) :neutral
       (and (<= 3.0 target) (< target 4.0)) :agree
       (<= 4.0 target 5.0) :strongly-agree
       (= target 42) :the-answer
       :else :do-not-care)
    Test cases:
      (use '[clojure.test :only (deftest is run-tests)])
      (deftest unit-tests
        (letfn [(test-range-case [target]
                                 (range-case target
                                             [0.0 :ie 1.0] :greatly-disagree
                                             [1.0 :ie 2.0] :disagree
                                             [2.0 :ie 3.0] :neutral
                                             [3.0 :ie 4.0] :agree
                                             [4.0 :ii 5.0] :strongly-agree
                                             42 :the-answer
                                             :else :do-not-care))]
      (is (= (test-range-case 0.0) :greatly-disagree))
      (is (test-range-case 0.5) :greatly-disagree)
      (is (test-range-case 1.0) :disagree)
      (is (test-range-case 1.5) :disagree)
      (is (test-range-case 2.0) :neutral)
      (is (test-range-case 2.5) :neutral)
      (is (test-range-case 3.0) :agree)
      (is (test-range-case 3.5) :agree)
      (is (test-range-case 4.0) :strongly-agree)
      (is (test-range-case 4.5) :strongly-agree)
      (is (test-range-case 5.0) :strongly-agree)
      (is (test-range-case 42) :the-answer)
      (is (test-range-case -1) :do-not-care)))
    (run-tests)"
  `(cond
    ~@(loop [cases cases ret []]
        (cond
         (empty? cases)
         ret

         (odd? (count cases))
         (throw (IllegalArgumentException.
                 (str "no matching clause: " (first cases))))

         (= :else (first cases))
         (recur (drop 2 cases) (conj ret :else (second cases)))

         (vector? (first cases))
         (let [[lower-bound operator upper-bound] (first cases)
               clause (second cases)

               [condition clause]
               (case operator
                     :ii `((<= ~lower-bound ~target ~upper-bound) ~clause)
                     :ie `((and (<= ~lower-bound ~target)
                                (< ~target ~upper-bound)) ~clause)
                     :ei `((and (< ~lower-bound ~target)
                                (<= ~target ~upper-bound)) ~clause)
                     :ee `((< ~lower-bound ~target ~upper-bound) ~clause)
                     (throw (IllegalArgumentException.
                             (str "unknown operator: " operator))))]
           (recur (drop 2 cases) (conj ret condition clause)))

         :else
         (let [[condition clause]
               `[(= ~target ~(first cases)) ~(second cases)]]
           (recur (drop 2 cases) (conj ret condition clause)))))))
(定义宏范围案例[目标和案例]
“将目标与一组范围或常量值进行比较,然后返回
第一个匹配的。如果不匹配,则存在
值:否则,返回该目标。每个范围由一个包含
3个术语:下限、运算符和上限。运算符必须
是:ii、:ie、:ei或:ee中的一个,表示范围比较
应该是包容的,包容的,排斥的,排斥的,,
或者分别是独家的。
例子:
(射程情况目标)
[0.0:ie 1.0]:非常不同意
[1.0:ie 2.0]:不同意
[2.0:ie 3.0]:中性
[3.0:ie 4.0]:同意
[4.0:ii 5.0]:强烈同意
42:答案是什么
:否则:不在乎)
扩展到
(续)
(和(一些想法:

  • 为操作符设置一个默认值(例如:ie在典型问题中可能是最自然的)
  • 将其中一个边界默认为上一个或下一个上限/下限,这样就不需要重复相同的边界值
  • 考虑ifs而不是cond,这样您就可以进行区间二分法(如果您期望非常多的情况,这将是一个性能胜利)
另一种方法是使宏在案例级别工作,如下所示:

(cond
  (in-range target [0.0 1.0]) :greatly-disagree)
  (in-range target [1.0 2.0]) :disagree)
  ...)

我个人喜欢这样做,因为如果需要,您可以将范围测试与其他谓词混合使用。

我也会选择更详细但不太难看的内容

 (range-case target
   [(<= 0.0) (< 1.0)] :greatly-disagree
   [(<= 1.0) (< 2.0)] :disagree
   [(<= 2.0) (< 3.0)] :neutral
   [(<= 3.0) (< 4.0)] :agree
   (<= 4.0 5.0)       :strongly-agree
   42 :the-answer
   :else :do-not-care)
(范围案例目标
[(我对它的最初看法:

(defn make-case [test val]
  (if (vector? test)
    `((and ~@(for [[lower comp upper] (partition 3 2 test)]
               (list comp lower upper)))
      ~val)

    (list :else val)))

(defmacro range-case [& cases]
  (let [cases (partition 2 cases)]
    `(cond ~@(mapcat (partial apply make-case) cases))))
这需要对语法稍作修改,如下所示:

(range-case 
 [0.0 <= x < 1.0] :greatly-disagree
 [1.0 <= x < 2.0] :disagree
 [2.0 <= x < 3.0] :neutral
 [3.0 <= x < 4.0] :agree
 [4.0 <= x <= 5.0] :strongly-agree
 [42 = x] :the-answer
 :else :do-not-care)

@米凯拉:这和常规的
cond
:-)一样冗长。不过,我喜欢你的前两条评论。我会进一步探讨它们。作为一个一般原则,我不认为尽量减少冗长是一个特别有用的目标……我会选择可读性、可维护性和一致性,而不仅仅是保留一些键入字符:-)@米凯拉:我同意。我是个聪明人。不过,你对
(范围内…
的建议应该是另一个宏。它本身非常有用。@米凯拉:关于你对隐式下限的建议,在第一个case子句中,如果省略下限,它可以(隐式地)是
(-Integer/MAX\u Integer)
(没有实际添加一个
(<-Integer/MAX\u Integer)目标)
到不断增长的
cond
)。我不确定我是否理解“区间二分法”“你提到过。@Ralph区间二分法只是意味着你先取中间的情况-如果目标值小于2.0,那么你根本不需要检查任何较高的情况,因为它们必须是假的。因此,你只需一次测试就可以消除一半的情况。它基本上将复杂性从O(n)变成O(logn)如果您有n个案例,那么如果n较大,则值得这么做。您的建议,再加上mikera对“默认”运算符和隐式下限的建议,可能会很有用。我们将进行调查。甚至可能会调查
[LB-UB]
[LB
[
,以及
[:ii
:ie
:ei
:ee
,默认运算符为
,谢谢。“所以我怀疑您是否真的需要宏。”这更像是编写宏的练习,而不是真正有用的东西。
(range-case 
 [0.0 <= x < 1.0] :greatly-disagree
 [1.0 <= x < 2.0] :disagree
 [2.0 <= x < 3.0] :neutral
 [3.0 <= x < 4.0] :agree
 [4.0 <= x <= 5.0] :strongly-agree
 [42 = x] :the-answer
 :else :do-not-care)
(defn ?? [& xs]
  (every? (fn [[lower comp upper]]
            (comp lower upper))
          (partition 3 2 xs)))

(cond
  (?? 0.0 <= x < 1.0) :greatly-disagree
  (?? 1.0 <= x < 2.0) :disagree
  (?? 2.0 <= x < 3.0) :neutral
  (?? 3.0 <= x < 4.0) :agree
  (?? 4.0 <= x <= 5.0) :strongly-agree
  (= 42 x) :the-answer
  :else :do-not-care)