Соответствующие элементы на определенной позиции списков - PullRequest
0 голосов
/ 04 января 2019

Относительно этого вопроса Я бы хотел посчитать количество совпадений между элементами двух разных списков списков в определенной позиции.

Например:

'((ab c ) (de c ) (fgh))' ((aek) (lf c ) (gp c ))

будет возвращать 2 всякий раз, когда мы указываем соответствующую позицию как третью одну в каждом списке (независимо от того, что содержат другие позиции).

Есть ли функция, выполняющая эту операцию? Я не могу найти это. Спасибо.

1 Ответ

0 голосов
/ 05 января 2019

Решение

Я не знаю никаких готовых функций. Поэтому я написал собственный.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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)))))

С этим мы можем попробовать lols с символами:

(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

lols с номерами в качестве элементов:

(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

и lols со строками в качестве элементов:

(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

даже смешанные lols:

(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")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...