List 为什么不';我的if语句不起作用吗?

List 为什么不';我的if语句不起作用吗?,list,if-statement,clojure,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 (

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

)

尝试此方法,并请注意建议的代码格式-不要单独在一行中留下单独的括号,它们与其他编程语言中的
{}
不同:

(defn occursIn [x y]
  (if (empty? y) 
      "False"
      (if (= x (first y))
          "True"  
          (occursIn x (rest y)))))
您忘记在
y
列表中首先调用
,如下所示:

(first y)
另外,请注意,基本情况并不像您预期的那样工作。改为使用此测试:

(empty? y)
为了完整起见,这里有一种更惯用的方法来编写相同的过程-但是要注意@omiel在注释中指出的边缘情况,如果
x
false
nil
,这将不起作用:

(defn occursIn [x y]
  (if (some #(= x %) y)
      "True"
      "False"))
(defn occursIn [x y]
    (if (empty? y) 
        false
        (if (= x (first y))
            true
            (occursIn x (next y))
        )
    )
)

(occursIn nil ())
; false
一个更好的解决方案,没有边缘案例-正如@mange在评论中所建议的:

(defn occursIn [x y]
  (if (every? #(not= x %) y)
      "False"
      "True"))

尝试此方法,并请注意建议的代码格式-不要单独在一行中留下单独的括号,它们与其他编程语言中的
{}
不同:

(defn occursIn [x y]
  (if (empty? y) 
      "False"
      (if (= x (first y))
          "True"  
          (occursIn x (rest y)))))
您忘记在
y
列表中首先调用
,如下所示:

(first y)
另外,请注意,基本情况并不像您预期的那样工作。改为使用此测试:

(empty? y)
为了完整起见,这里有一种更惯用的方法来编写相同的过程-但是要注意@omiel在注释中指出的边缘情况,如果
x
false
nil
,这将不起作用:

(defn occursIn [x y]
  (if (some #(= x %) y)
      "True"
      "False"))
(defn occursIn [x y]
    (if (empty? y) 
        false
        (if (= x (first y))
            true
            (occursIn x (next y))
        )
    )
)

(occursIn nil ())
; false
一个更好的解决方案,没有边缘案例-正如@mange在评论中所建议的:

(defn occursIn [x y]
  (if (every? #(not= x %) y)
      "False"
      "True"))

Oscar的答案是正确且有用的,但我只是想提供一个替代答案,演示如何使函数更接近原始函数,同时仍然是相对惯用的clojure:

(defn occurs-in [needle haystack]
  (if-let [[head & tail] (seq haystack)]
    (if (= needle head)
      "True"
      (recur needle tail))
    "False"))
在这种情况下,我:

  • 使用解构来提取序列上的
    头部
    /
    尾部
    ,而不是使用
    第一个
    /
    剩余
  • 仅当
    haystack
    为非空时(即如果
    (seq haystack)
    为truthy),才使用
    if let
    绑定这些名称
  • 使用
    recur
    而不是
    中出现,以确保此函数将被编译为使用尾部调用消除,因此它将在不使用额外堆栈帧的情况下再次调用自身

    对于小输入来说,这可能不是一个问题,但对于像
    (发生在1000000(范围))


在惯用命名方面,我使用了
出现在
而不是
出现在
:通常lisp使用连字符(
lisp case
)而不是驼色case(
camelCase
)作为名称。

Oscar的回答是正确和有用的,但我只是想提供一个替代答案,演示如何制作一个更接近原始函数的函数,同时仍然是相对惯用的clojure:

(defn occurs-in [needle haystack]
  (if-let [[head & tail] (seq haystack)]
    (if (= needle head)
      "True"
      (recur needle tail))
    "False"))
在这种情况下,我:

  • 使用解构来提取序列上的
    头部
    /
    尾部
    ,而不是使用
    第一个
    /
    剩余
  • 仅当
    haystack
    为非空时(即如果
    (seq haystack)
    为truthy),才使用
    if let
    绑定这些名称
  • 使用
    recur
    而不是
    中出现,以确保此函数将被编译为使用尾部调用消除,因此它将在不使用额外堆栈帧的情况下再次调用自身

    对于小输入来说,这可能不是一个问题,但对于像
    (发生在1000000(范围))


在惯用命名方面,请看我使用了
出现在
中而不是
出现在
中:通常lisp使用连字符(
lisp case
)而不是驼峰大小写(
camelCase
)作为名称。

在我们做任何其他事情之前,让我们用布尔
True
替换
“True”
带有布尔值
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所说,在参数
y
上调用
first
时,在
first y
周围缺少括号。就目前而言

(= x first y)
测试
x
first
y
是否都相等。只有一种方法可以做到这一点:

(occursIn first first)
; true
。。。不是我们想要的。因此,让我们先调用
而不是比较它:

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

(occursIn 2 aList)
; true
它起作用了

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

(occursIn 2 ())
; false
。。。但这仅仅是因为
下一个
调用将
()
变为
nil
:恶心

并且它会失火搜索
nil

(occursIn nil ())
; true
。。。因为
(first())
返回
nil
并且
nil
在被要求成为序列时假装是
()
(这称为nil双关语),所以
(first nil)
nil

因此,在Oscar之后,我们最好再次测试
y
是否为空,而不是
nil

(defn occursIn [x y]
  (if (some #(= x %) y)
      "True"
      "False"))
(defn occursIn [x y]
    (if (empty? y) 
        false
        (if (= x (first y))
            true
            (occursIn x (next y))
        )
    )
)

(occursIn nil ())
; false
逻辑现在是正确的。让我们使用
而不是
if
s,并使用显式
true
false
结果(在我看来,这是一种代码味道)来更清楚地说明这一点:

代码现在很容易读取。为此,我们使用了正确的布尔值

只有两个变化: