列表中特定位置的匹配元素

时间:2019-01-04 16:51:03

标签: list pattern-matching racket

this question相关,我想计算某个位置上两个不同列表列表的元素之间的匹配次数。

例如:

  

'((ab c )(de c )(fgh))'((aek)(lf c )(gp < strong> c ))

每当我们在每个列表中将匹配位置指定为第三个位置时,

都会返回2(无论其他位置包含什么)。

是否有执行此操作的功能?我找不到它。谢谢。

1 个答案:

答案 0 :(得分:2)

解决方案

我不知道任何现成的功能。所以我写了自己的。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; filter list of list by inner list element position
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (position-filter lol pos)       
  (map (lambda (l) (list-ref l pos)) lol))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; intersect two lists (duplicate-preserved)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; without duplicates would be `set-intersect`

(define (list-intersect l1 l2 (acc '()) (test equal?))
  (cond ((or (null? l1) (null? l2)) (reverse acc))
        ((member (car l1) l2 test)
         (list-intersect (cdr l1) (remove (car l1) l2) (cons (car l1) acc) test))
        (else (list-intersect (cdr l1) l2 acc test))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; intersect two position-filtered lols
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (lol-intersect-at-pos lol-1 lol-2 pos)
  (let ((l1 (position-filter lol-1 pos))
        (l2 (position-filter lol-2 pos)))
    (list-intersect l1 l2)))

;; you can count then how many elements are common by `length`

就是这样。

测试

由于我太“懒惰”,无法用字符串编写大声笑,所以我编写了一个便捷函数:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; convert lol elements to strings
;; convenience function
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require racket/format) ;; for ~a
(define to-string ~a)

(define (as-strings nested-list (acc '()))
  (cond ((null? nested-list) (reverse acc))
        ((list? (car nested-list))
         (as-strings (cdr nested-list)
                     (cons (as-strings (car nested-list))
                           acc)))
        (else
         (as-strings (cdr nested-list)
                          (cons (to-string (car nested-list))
                                acc)))))

配备了这个,我们可以尝试使用符号的笑话:

(lol-intersect-at-pos '((a b c) (d e c) (f g h)) 
                      '((a e k) (l f c) (g p c)) 
                      2)
;;'(c c) ;; length is 2

以数字为元素滚动:

(lol-intersect-at-pos '((1 2 3) (4 5 3) (6 7 8)) 
                      '((1 5 19) (18 7 3) (29 39 3)) 
                      2)
;;'(3 3) ;; length is 2

和以字符串为元素的滚动:

(lol-intersect-at-pos (as-strings '((a b c) (d e c) (f g h))) 
                      (as-strings '((a e k) (l f c) (g p c))) 
                      2)
;;'("c" "c") ;; length is 2

均匀混合的滚动:

(lol-intersect-at-pos '((a b c) (a b "c") (d e 3) (f g "3"))
                      '((d c c) ("a" "b" c) (1 3 3) (2 4 3))
                      2)
;;'(c 3) ;; length of that is 2

更复杂的排序解决方案(需要转换symbol->string及其所有复杂性)

在此之前,我写了这个。我把它留给历史。

#lang racket

(define (get-position-values lol pos)       ; to extract elements at pos in inner lists
  (map (lambda (l) (list-ref l pos)) lol))

; to determine all elements common between two lists
; set-intersect would remove duplicates, so I had to write an list-intersect
(define (list-intersect l1 l2 (acc '()) (test-equality equal?) (test-smaller <))
  (let ((lst1 (sort l1 test-smaller))
        (lst2 (sort l2 test-smaller)))
    (cond ((or (null? lst1) (null? lst2)) (reverse acc))
          ((test-equality (car lst1) (car lst2))
           (list-intersect (cdr lst1) (cdr lst2) (cons (car lst1) acc) test-equality test-smaller))
          ((test-smaller (car lst1) (car lst2))
           (list-intersect (cdr lst1) lst2 acc test-equality test-smaller))
          (else
           (list-intersect lst1 (cdr lst2) acc test-equality test-smaller)))))

; to determine all elements common between two list of lists at position pos
; transformer is the function applied to the extracted list elements (necessary when symbols are used,
; since symbols don't have a test-smaller test, only equality test, but sorting would improve performance ...
; so this function doesn't allow to mixup strings and symbols, because symbols would be converted to strings
; so indistinguishable from strings when applying equality test.
; if one wants better equality test, then one has to construct a more complex test-smaller test function which
; can handle strings, symbols, numbers etc. - and one needs also such a more complex test-equality function -
; and then the transformer can be the identity function.
(define (match-element-lol-pos lol-1 lol-2 pos (test-equality string=?) (test-smaller string<?) (transformer symbol->string))
  (let* ((l1 (get-position-values lol-1 pos))
         (l2 (get-position-values lol-2 pos))
         (sl1 (map transformer l1))
         (sl2 (map transformer l2))
         (commons (list-intersect sl1 sl2 '() test-equality test-smaller)))
    (values (length commons) commons)))

然后您可以将其应用于示例列表对列表。

(match-element-lol-pos '((a b c) (d e c) (f g h)) '((a e k) (l f c) (g p c)) 2)
; 2 for third element of inner lists!

哪个给:

;; 2
;; '("c" "c")

以数字为元素的列表列表,可以这样调用:

(match-element-lol-pos '((1 2 3) (4 5 3) (6 7 8)) '((1 5 19) (18 7 3) (29 39 3)) 2 = < identity)
;; 2
;; '(3 3)

以字符串为元素的列表列表,一个这样的调用。 为了方便起见,我编写了一个函数as-strings,该函数将嵌套列表中的所有元素转换为字符串。我实在太懒了,无法将""包裹在每个符号中……

;; convert all list elements of any nested-list into strings
(require racket/format) ;; for ~a
(define to-string ~a)

(define (as-strings nested-list (acc '()))
  (cond ((null? nested-list) (reverse acc))
        ((list? (car nested-list)) (as-strings (cdr nested-list) (cons (as-strings (car nested-list)) acc)))
        (else (as-strings (cdr nested-list) (cons (to-string (car nested-list)) acc)))))

因此可以这样使用:

(match-element-lol-pos (as-strings '((a b c) (d e c) (f g h))) (as-strings '((a e k) (l f c) (g p c))) 2 string=? string<? identity)
;; 2
;; '("c" "c")