简体   繁体   English

在 LISP 中查找二维数组中的最大元素

[英]Finding the Max element in a 2d array in LISP

So, I wrote the following program to determine the max element in a 2D array using LISP.因此,我编写了以下程序来使用 LISP 确定二维数组中的最大元素。 Note, this is my first time using the language, so I am unfamiliar with many aspects.请注意,这是我第一次使用该语言,所以我很多方面都不熟悉。

The function should return the coordinates of the largest element in row-major order. function 应返回行优先顺序中最大元素的坐标。 In the below example, the largest element, 7, is found at index (2, 4).在下面的示例中,最大的元素 7 位于索引 (2, 4) 处。 However, when the program is executed, it returns (3, 15).但是,当程序执行时,它返回 (3, 15)。

It seems to be starting the index at 1, rather than 0, for the row.该行的索引似乎从 1 而不是 0 开始。 Additionally, it seems to be counting all indexes up to 15, where the max element is found.此外,它似乎将所有索引计数到 15,其中找到了最大元素。 I am unsure how to fix this issue.我不确定如何解决这个问题。

(defun find-max-location (x)
  (let (
      (maxval -100)
      (loc nil)
      (cur 0)
      (cur2 0)
      (k 1)
      (l 1)
      (f 0))
    (loop for i in x do
          (loop for j in i do
                (if (> j maxval)
                    (progn
                      (setf cur k)
                      (setf cur2 l)
                      (setf maxval j))
                  )
                (setf l (+ l 1))
                )
          (setf k (+ k 1))
          )
    (list cur cur2)))

(find-max-location '((0 1 0 0 1) (0 2 2 0 0) (3 0 1 4 7) (0 1 2 0 0) (1 2 1 0 3)))

The error is very simple: you don't reinitialize tha value of l each time the inner iteration is started.错误非常简单:每次开始内部迭代时,您都没有重新初始化l的值。 For the indexes, instead, if you want to start from 0 , then you should initialize them with 0 , and not with 1 .相反,对于索引,如果你想从0开始,那么你应该用0初始化它们,而不是用1 Here is a rewriting of the code, in which I have removed the unused variables loc and f , used when instead of the one-branch if , and used a more conventional way of writing in Common Lisp:这是代码的重写,其中我删除了未使用的变量locf ,使用when而不是单分支if ,并使用 Common Lisp 中更传统的编写方式:

(defun find-max-location (x)
  (let ((maxval -100)
        (cur 0)
        (cur2 0)
        (k 0)
        l)
     (loop for i in x
           do (setf l 0)
              (loop for j in i         
                    do (when (> j maxval)
                         (setf cur k)
                         (setf cur2 l)
                         (setf maxval j))
                       (setf l (+ l 1)))
              (setf k (+ k 1)))   
     (list cur cur2)))

(find-max-location '((0 1 0 0 1) (0 2 2 0 0) (3 0 1 4 7) (0 1 2 0 0) (1 2 1 0 3)))   ; => (2 4)

Note that setf can perform multiple assignments, so the code can be simplified as:注意setf可以进行多次赋值,所以代码可以简化为:

(defun find-max-location (x)
  (let ((maxval -100)
        (cur 0)
        (cur2 0)
        (k 0)
        l)
    (loop for i in x
          do (setf l 0)
             (loop for j in i          
                   do (when (> j maxval)
                        (setf cur k
                              cur2 l
                              maxval j))
                      (setf l (+ l 1)))
             (setf k (+ k 1)))    
     (list cur cur2))

Finally, there are very convenient mechanisms in loop that allow the initialization of variables ( with ) and the looping of variables “in parallel” with the iteration variables.最后, loop中有非常方便的机制,允许变量初始化 ( with ) 和变量循环与迭代变量“并行”。 So here is the final iterative version of the function (with the use of when as loop keyword):所以这是 function 的最终迭代版本(使用when作为循环关键字):

(defun find-max-location (x)
  (loop with maxval = -100
        and cur = 0
        and cur2 = 0
        for i in x
        for k from 0
        do (loop for j in i
                 for l from 0
             when (> j maxval)
               do (setf cur k
                        cur2 l
                        maxval j))
        finally (return (list cur cur2))))

A very good starting point to master the complexity of loop is the chapter “Loop for Black Belts" of the free book “Practical Common Lisp” . Another excellent resource to learn Common Lisp is the “Common Lisp Cookbook” .掌握loop复杂性的一个很好的起点是免费书籍“Practical Common Lisp”中的“黑带循环”一章。学习 Common Lisp 的另一个优秀资源是“Common Lisp Cookbook”

Finally, it is worth to note that Common Lisp has arrays, including 2D arrays, so that you can represent them directly, instead that with lists of lists, as in your example.最后,值得注意的是 Common Lisp 有 arrays,包括 2D arrays,因此您可以直接表示它们,而不是像您的示例中那样使用列表列表。

It seems that you're thinking about this in a very procedural manner.看来您正在以一种非常程序化的方式考虑这个问题。 Recursion is your friend here.递归是你的朋友。

How would we find the max and its position in a simple list?我们如何在一个简单列表中找到最大值及其 position? We'd recursively count up from 0 , evaluating each element to see if it's greater than the current max or if max hasn't yet been set.我们将从0开始递归计数,评估每个元素以查看它是否大于当前max ,或者是否尚未设置max We'd use the max parameter to the function to update this information.我们将使用 function 的max参数来更新此信息。 Hitting the end of the list we'd return the max .到达列表末尾我们将返回max

(defun find-max-in-row (lst &optional (pos 0) (max '()))
    (cond ((null lst) 
           max)
          (or (null max) (> (car lst) (car max))
           (find-max-in-row (cdr lst) (1+ pos) (list (car lst) pos)))
          (t 
           (find-max-in-row (cdr lst) (1+ pos) max))))

(defvar foo '(1 6 8 2 7))

(format t "~a~%" (find-max-in-row foo))

Prints:印刷:

(8 2)

This logic can be adapted to find the column where a max value occurs.可以修改此逻辑以查找出现最大值的列。 A starting point would be to map this function to your list of rows.起点是 map 这个 function 到你的行列表。

(format t "~a~%" 
     (mapcar #'find-max-in-row 
             '((0 1 0 0 1) 
               (0 2 2 0 0) 
               (3 0 1 4 7) 
               (0 1 2 0 0) 
               (1 2 1 0 3))))

Prints:印刷:

((1 1) (2 1) (7 4) (2 2) (3 4))

I would loop in a nested way through the list-of-list (lol) and setf the values when bigger than current max-val :我会以嵌套方式循环遍历列表列表(lol)并在大于当前max-val setf设置值:

(defun find-max-location (lol)
  "Find max element's coordinate in a lol"
  (let ((max-val (caar lol))
        (max-coord '(0 . 0))) ;; start with first element
    (loop for il in lol
          for i from 0
          do (loop for e in il
                   for j from 0
                   when (> e max-val)
                     do (setf max-val e
                              max-coord (cons i j))))
    (values max-coord max-val)))

This function can handle irregular list-of-lists.这个 function 可以处理不规则的列表列表。

Using values , you can decide whether just to use only the coordinates or to also capture the max-value itself.使用values ,您可以决定是仅使用坐标还是也捕获max-value本身。

;; usage:

(find-max-location '((0 1 9) (4 9) (6 7 8)))
;; => (0 . 2)
;; => 9

;; capture max-val too:
(multiple-value-bind (coord val) (find-max-location '((0 1 9) (4 9) (6 7 8)))
  (list val (car coord) (cdr coord)))
;; => (9 0 2)

;; or destructure coord:
(multiple-value-bind (coord val) (find-max-location '((0 1 9) (4 5) (6 7 8)))
  (destructuring-bind (x . y) coord
    (list val x y)))

The first max-val is the very first element in this list-of-list .第一个max-val是这个list-of-list第一个元素。

Here is another answer that defines a higher-order function indexed-list-fold , similar to folding functions like reduce or fold_left in other functional languages.这是另一个定义高阶 function indexed-list-fold的答案,类似于其他功能语言中的折叠函数,如reducefold_left

It's purpose is to take a function that works on a accumulator , and element and its index , to produce the next value for the accumulator .它的目的是采用 function 在accumulatorelement及其索引上工作,为accumulator产生下一个值。 I define it as follows, with loop :我将其定义如下,带loop

(defun indexed-list-fold (function accumulator list)
  (loop
    :for acc = accumulator :then res
    :for elt :in list
    :for idx :from 0
    :for res = (funcall function acc elt idx)
    :finally (return acc)))

Notice the use of for/then construct and how the order of the clauses matter: for elt in list may interrupt the iteration when reaching the end of the list, so it is important that acc is computed first.请注意for/then构造的使用以及子句的顺序如何重要: for elt in list在到达列表末尾时可能会中断迭代,因此首先计算acc很重要。 This is especially important if the list is empty initially, otherwise its value would be always nil and not accumulator .如果列表最初为空,这一点尤其重要,否则它的值将始终为nil而不是accumulator

Let's write a function that finds the maximum value and its location, in a list:让我们编写一个 function 来查找列表中的最大值及其位置:

(defun find-max-location-in-list (list)
  (flet ((fold (max.pos elt idx)
           (let ((max (car max.pos)))
             (if (or (null max) (< max elt))
                 (cons elt idx)
                 max.pos))))
    (indexed-list-fold #'fold (cons nil nil) list)))

For example:例如:

> (find-max-location-in-list '(1 3 8 3 12 -4 -200))
(12 . 4)

The maximum value is 12 at position 4.最大值为 12,位于 position 4。

In order to generalize this to a list of lists (a tree), the fold function must be able to reference itself in a recursive call to indexed-list-fold , so it is now declared with labels :为了将其概括为列表列表(一棵树), fold function 必须能够在对indexed-list-fold的递归调用中引用自身,因此现在使用labels声明它:

(defun find-max-location-in-tree (tree)
  (labels ((fold (max.pos elt idx)
             (let ((idx (alexandria:ensure-list idx)))
               (etypecase elt
                 (real (let ((max (car max.pos)))
                         (if (or (null max) (< max elt))
                             (cons elt idx)
                             max.pos)))
                 (list 
                  (indexed-list-fold (lambda (max.pos elt child)
                                       (fold max.pos elt (cons child idx)))
                                     max.pos
                                     elt))))))
    (indexed-list-fold #'fold (cons nil nil) tree)))

The logic is the same as before, except that elt may now be a list itself, and idx is a stack of indices.逻辑和以前一样,除了elt现在可能是一个列表本身,而idx是一堆索引。 For example:例如:

> (find-max-location-in-tree '(1 3 8 3 12 -4 -200))
(12 4)

Notice how the cdr of the result is a list.注意结果的cdr如何是一个列表。 For the example you provided:对于您提供的示例:

> (find-max-location-in-tree '((0 1 0 0 1)
                               (0 2 2 0 0)
                               (3 0 1 4 7)
                               (0 1 2 0 0)
                               (1 2 1 0 3)))
(7 4 2)

The maximum value is 7 at index 4 of the list at index 2.索引 2 处的列表的索引 4 处的最大值为 7。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM