If statement Racket新手:为什么if语句会影响回报?

If statement Racket新手:为什么if语句会影响回报?,if-statement,scheme,racket,If Statement,Scheme,Racket,我刚开始学习球拍,所以我仍在努力弄清楚这门语言的复杂性。我正在尝试在列表中实现自己的搜索功能。如果函数找到它,则返回索引,否则返回-1 (define (find-index list item) (if (equal? (length list) 1) (if (equal? (first list) item) 0 1) (if (equal? (first list) item) 0 (+ 1 (my-search (re

我刚开始学习球拍,所以我仍在努力弄清楚这门语言的复杂性。我正在尝试在列表中实现自己的搜索功能。如果函数找到它,则返回索引,否则返回-1

(define (find-index list item)
  (if (equal? (length list) 1)
      (if (equal? (first list) item) 0 1)
      (if (equal? (first list) item)
          0
          (+ 1 (my-search (rest list) item)))))
所以find index函数是一个递归函数,它遍历列表,寻找一个等价于“item”的项。我编写它是为了,如果列表中有4个元素,该函数可以返回0-4之间的任何数字

(define (my-search list item)
  (define length (my-length list))
  (define index (find-index list item))
  (if (= index length) -1 index))
我的想法是,如果find index函数返回一个等于列表长度的数字,这意味着该函数没有找到该项,因此我的搜索函数应该返回-1

然而,当我投入

(my-search (list "apple" "barbecue" "child" "demon" "enter") "fire")
我得到的结果是3,而不是-1。如果在If语句之前打印索引,则索引是3而不是5。如果

(if (= index length) -1 index))
不是我搜索功能的一部分,那个么一切都很好

我认为索引是函数本身的id,而不是函数的结果。但是,我不明白为什么这会影响我搜索的返回结果。有人愿意就这个问题解释一下吗


此外,欢迎任何风格批评。我想知道我是否没有遵循惯例。

这种奇怪的行为是由
find index
正在调用
my search
而调用
find index
(一种相互递归!)这一事实造成的。在某种程度上,额外的
if
会导致递归过早结束。解决方案:在
find index
过程中将对
my search
的调用替换为
find index

既然已经解决了这个问题,我们可以编写一个过程来在列表中查找元素的索引,或者发出未找到元素的信号,如下所示:

(define (find-index lst item)
  (cond ((empty? lst) #f)
        ((equal? (first lst) item) 0)
        (else
         (let ((result (find-index (rest lst) item)))
           (if result (add1 result) #f)))))
让我们看看上面的内容如何改进您的程序:

  • 构造具有多个条件的程序的首选方法是使用
    cond
  • 不应将
    列表
    用作参数名称,它与同名的内置过程冲突
  • 出于同样的原因,您不应该调用
    length
    本地定义
  • 使用
    length
    检查是否超出了列表不是一个好主意,一个构建良好的递归将解决这个问题,而无需再次迭代列表
  • 通常使用
    #f
    表示搜索过程没有找到它要查找的内容
  • 在结构良好的列表递归中,您应该检查列表是否为空,通常这是我们编写的第一个基本情况-如果传递了空列表,您的过程将失败
  • 我们使用
    let
    来声明局部变量,在这种情况下,避免调用递归两次是有意义的
  • 使用
    (add1 x)
    ,它比
    (+1 x)
但是等等,我们可以做得更好!上述解决方案可以用尾部递归方式重写;通过确保递归调用是我们做的最后一件事,我们的过程将使用常量空间,并且它将与传统编程语言中的循环一样高效。诀窍是传递一个额外的参数和要返回的值(在本例中是索引)。为了简洁起见,我将使用一个名为
let

(define (find-index lst item)
  (let loop ((lst lst) (idx 0))
    (cond ((empty? lst) #f)
          ((equal? (first lst) item) idx)
          (else (loop (rest lst) (add1 idx))))))
您可以验证这两个过程是否按照广告的方式工作:

(find-index (list "apple" "barbecue" "child" "demon" "enter") "fire")
=> #f
(find-index (list "apple" "barbecue" "child" "demon" "enter") "child")
=> 2

这就是我解决问题的方法

(define (find-index L item) ; L your list. item the item for which you want the index 
    (define (aux L res) ; Auxiliary function. L your list. item the item for which you want the index 
        (cond ((null? L) -1) ; No thing was found, return -1.
              ((eq? (car L) item) res) ; If the item is equal to the current item then return the position.
              (else (aux (cdr L) (add1 res))))) ; Move on to the next item in the list and increment the position.
(aux L 0)) ; Call of the auxiliary function that will be doing the job
试运行

(define L '(a b c d))
元素不在列表中

(find-index L 'e)
输出:-1

元素“d”


输出:3

这里是一个版本的
查找索引
,它尝试使用与原始示例相同的样式。我使用的不是
list
,而是
xs
(它是“x的列表”的缩写)

请注意,最好使用假值
#f
表示“未找到”


这是一个非常有用的回答。我很感激帮助我改进代码的要点。我还学到了尾部递归是正确的方法。但是,它仍然不能回答我的问题,为什么if语句会影响索引的结果,甚至在if语句之前。如果我删除if语句和println索引,索引是5(假设列表中找不到该项)。当if语句存在时,println索引变为3。不,递归结构正确。如果在列表中找不到该项,如果我的搜索函数的最后一条if语句不在列表中,我将得到5。
(定义(我的搜索列表项)(定义长度(我的长度列表))(查找索引列表项))
如果找不到该项,将返回我5,并显示大小列表4@VivienK我发现了窃听器。是的,您的递归肯定是不正确的:
find index
正在调用
my search
,它正在调用
find index
(一个相互递归!)。在某种程度上,额外的
if
会导致递归过早结束。解决方案:在
查找索引
过程中将对
我的搜索
的调用替换为
查找索引
。哦,我的上帝,谢谢你。我觉得自己像个疯子。感谢您花时间调试我的代码。
(find-index L 'd)
(define (find-index xs item)
  (if (empty? xs)
      -1                                                      ; not found
      (if (equal? (first xs) item)
          0                                                   ; found at beginning
          (let ([index-in-rest (find-index (rest xs) item)])  ; index in rest of list
            (if (= index-in-rest -1)
                -1                                            ; not found in rest of list
                (+ 1 index-in-rest))))))                      ; add 1 because we skipped 
                                                                the first element