列表函数

head函数用于获取列表的第一个元素,但它返回的内容仍然包含在列表中。如果我们想要从这个列表中实际地获取元素,我们需要以某种方式提取它。

单个元素列表仅计算该元素,因此我们可以使用该eval函数进行提取。我们还可以定义一些辅助函数来帮助提取列表的第一,第二和第三个元素。我们稍后会使用这些函数。

  1. ; First, Second, or Third Item in List
  2. (fun {fst l} { eval (head l) })
  3. (fun {snd l} { eval (head (tail l)) })
  4. (fun {trd l} { eval (head (tail (tail l))) })

几章前我们简要介绍了一些递归列表函数。当然,我们可以使用这种技术来定义更多函数。

为了找到列表的长度,我们可以递归它,为尾部的长度增加1。为了找到列表的nth元素,我们可以执行tail操作并进行倒计数直到我们到达0。要获取列表的最后一个元素,我们只需访问总长度减去1的位置的元素即可。

  1. ; List Length
  2. (fun {len l} {
  3. if (== l nil)
  4. {0}
  5. {+ 1 (len (tail l))}
  6. })
  1. ; Nth item in List
  2. (fun {nth n l} {
  3. if (== n 0)
  4. {fst l}
  5. {nth (- n 1) (tail l)}
  6. })
  1. ; Last item in List
  2. (fun {last l} {nth (- (len l) 1) l})

有许多其他有用的函数遵循相同的模式。我们可以定义用于获取和删除列表前多个元素的函数,或者用于检查值是否是列表元素的函数。

  1. ; Take N items
  2. (fun {take n l} {
  3. if (== n 0)
  4. {nil}
  5. {join (head l) (take (- n 1) (tail l))}
  6. })
  1. ; Drop N items
  2. (fun {drop n l} {
  3. if (== n 0)
  4. {l}
  5. {drop (- n 1) (tail l)}
  6. })
  1. ; Split at N
  2. (fun {split n l} {list (take n l) (drop n l)})
  1. ; Element of List
  2. (fun {elem x l} {
  3. if (== l nil)
  4. {false}
  5. {if (== x (fst l)) {true} {elem x (tail l)}}
  6. })

这些函数都遵循类似的模式。如果有某种方法来提取这种模式会很好,所以我们不必每次都需要写代码。例如,我们可能想要一种可以对列表的每个元素执行某些功能的方法。这是我们可以定义为map的函数 。它需要输入一些功能和一些列表。对于列表中的每个变量,只要是使用f的变量,都将其追加到列表的前面。然后它将被map用于列表的尾部。

  1. ; Apply Function to List
  2. (fun {map f l} {
  3. if (== l nil)
  4. {nil}
  5. {join (list (f (fst l))) (map f (tail l))}
  6. })

有了这个,我们可以做一些看起来有点像循环的整洁的东西。在某些方面,这个概念比循环更强大。我们可以考虑立即对所有元素进行操作,而不是考虑依次对列表的每个元素执行某些功能。我们映射列表而不是更改每个元素

  1. lispy> map - {5 6 7 8 2 22 44}
  2. {-5 -6 -7 -8 -2 -22 -44}
  3. lispy> map (\ {x} {+ x 10}) {5 2 11}
  4. {15 12 21}
  5. lispy> print {"hello" "world"}
  6. {"hello" "world"}
  7. ()
  8. lispy> map print {"hello" "world"}
  9. "hello"
  10. "world"
  11. {() ()}
  12. lispy>

我们可以改变一下这个想法,这是一种filter功能,它接受一些功能条件,并且仅包括与该条件匹配的列表项。

  1. ; Apply Filter to List
  2. (fun {filter f l} {
  3. if (== l nil)
  4. {nil}
  5. {join (if (f (fst l)) {head l} {nil}) (filter f (tail l))}
  6. })

这就是它在实践中的样子。

  1. lispy> filter (\ {x} {> x 2}) {5 2 11 -7 8 1}
  2. {5 11 8}

某些循环并不完全作用于列表,而是累积一些循环或将列表压缩为单个值。这些是诸如求和和阶乘之类的循环。这些可以与我们已经定义的len函数的表达非常相似。

这些被称为折叠,它们的工作方式如下。提供了一个函数f,一个基值z和一个列表,它们将列表l中的每个元素与总值合并,从基值开始。

  1. ; Fold Left
  2. (fun {foldl f z l} {
  3. if (== l nil)
  4. {z}
  5. {foldl f (f z (fst l)) (tail l)}
  6. })

使用折叠,我们可以非常优雅的方式定义sumproduct功能。

  1. (fun {sum l} {foldl + 0 l})
  2. (fun {product l} {foldl * 1 l})