我对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))
)
)
)
答案 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)
测试x
,first
和y
是否相等。这种情况只有一种:
(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 ())
返回nil
而nil
在被要求成为序列时假装为()
(这称为 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
逻辑现在是正确的。让我们使用and
和or
代替if
来明确true
和false
结果(代码气味,在我看来),让我们更清楚:< / 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}}