为什么我的if语句不起作用?

时间:2014-06-26 22:54:20

标签: list if-statement clojure

我对Clojure非常陌生,对函数式编程也很陌生。我希望这会返回真或假但它只是无限递归而且它似乎根本不是真的。

我的测试数据集如下:

(def y (list 1 2 3 4)) ; and I'm passing in 2 for X.


(defn occursIn [x y]
    (if (= y nil) 
        "False"
        (if (= x first y )
            "True"  
            (occursIn x (rest y))
        )
    )

)

3 个答案:

答案 0 :(得分:5)

试试这个,请注意建议的格式化代码的方法 - 不要让孤独的括号单独留在一行,他们就像{{1在其他编程语言中:

{}

您忘记在(defn occursIn [x y] (if (empty? y) "False" (if (= x (first y)) "True" (occursIn x (rest y))))) 列表上拨打first,如下所示:

y

另外,请注意基本情况并非按预期工作。请改用此测试:

(first y)

为了完整性'为了这个过程,写一个相同的程序是一种更惯用的方式 - 但要注意@omiel在评论中指出的边缘情况,如果(empty? y) x,则无法工作false

nil

一个更好的解决方案,没有边缘案例 - 正如@mange在评论中所建议的那样:

(defn occursIn [x y]
  (if (some #(= x %) y)
      "True"
      "False"))

答案 1 :(得分:2)

奥斯卡的答案是正确和有帮助的,但我只是想我提供一个替代答案,演示如何制作一个更接近原始功能的功能,同时仍然是相对惯用的术语:< / p>

(defn occurs-in [needle haystack]
  (if-let [[head & tail] (seq haystack)]
    (if (= needle head)
      "True"
      (recur needle tail))
    "False"))

在这种情况下我是:

  • 使用解构来提取head / tail,而不是在序列上使用first / rest
  • 仅在if-let非空(即haystack是真实的)时才使用(seq haystack)绑定这些名称。
  • 使用recur而不是occurs-in来确保编译此函数以使用尾调用消除,因此它会在不使用额外堆栈帧的情况下再次调用自身。

    对于小型输入而言,这可能不是一个问题,但要使其适用于(occurs-in 1000000 (range))

  • 之类的调用至关重要

就惯用命名而言,请参阅我使用occurs-in代替occursIn:通常lisps使用连字符(lisp-case)而不是驼峰({{1} })为名字。

答案 2 :(得分:2)

在我们做其他事情之前,让我们用布尔"True"替换true,用布尔"False"替换false。这样做的原因将变得明显。

根本问题是rest永远不会返回nil。它返回空列表()(rest ())()。因此,您将获得无休止的递归调用序列,最终将堆栈顶部排除在外。

next会返回nil rest返回()。因此,请使用next代替rest,至少我们会得到答案:

(def aList (list 1 2 3 4))

(defn occursIn [x y]
    (if (= y nil) 
        false
        (if (= x first y)
            true
            (occursIn x (next y))
        )
    )
)

(occursIn 2 aList)
; false

......但错误的答案。为什么?正如@OscarLopez所说,你在first y附近缺少括号,以便在first参数上调用y。就目前而言,

(= x first y)

测试xfirsty是否相等。这种情况只有一种:

(occursIn first first)
; true

......不是我们想要的。所以,我们打电话给first而不是比较它:

(defn occursIn [x y]
    (if (= y nil) 
        false
        (if (= x (first y))
            true
            (occursIn x (next y))
        )
    )
)

(occursIn 2 aList)
; true

有效。

实际上,基本情况确实有效:

(occursIn 2 ())
; false

...但只是因为next来电()变为nil:queasy。

它确实不耐烦地搜索nil

(occursIn nil ())
; true

...因为(first ())返回nilnil在被要求成为序列时假装为()(这称为 nil punning ),(first nil)nil

所以,再次关注奥斯卡,我们最好测试y是否为空,而不是nil

(defn occursIn [x y]
    (if (empty? y) 
        false
        (if (= x (first y))
            true
            (occursIn x (next y))
        )
    )
)

(occursIn nil ())
; false

逻辑现在是正确的。让我们使用andor代替if来明确truefalse结果(代码气味,在我看来),让我们更清楚:< / p>

(defn occursIn [x y]
    (and (not (empty? y))
         (or (= x (first y))
             (occursIn x (next y))
         )
    )
)

代码现在可以轻松读取。这样做是为了使用正确的布尔值。

只有两个更改:

  • 如果您查找empty?,您会发现(empty? y)表示(not (seq y)),因此(not (empty? y))表示(not (not (seq y))),这是(seq y) 相当于occursIn
  • recur的递归调用是最后发生的事情:它 据说是在尾部位置。因此,我们可以替换它 (defn occursIn [x y] (and (seq y) (or (= x (first y)) (recur x (next y)) ) ) ) 。这不会消耗堆栈,因此对长度没有限制 可以搜索的序列。

所以我们最终得到......

{{1}}