Scheme 球拍对的结构

Scheme 球拍对的结构,scheme,racket,Scheme,Racket,我有以下孩子及其父亲姓名的数据: sally, THOMAS alfred, JOHNSON peter, SIMON dick, THOMAS harry, JOHNSON eliz, SIMON 我想有一个函数,可以找到某个孩子的兄弟姐妹。例如(兄弟姐妹“哈里”)应该返回“阿尔弗雷德”。球拍的哪个结构最适合这个?我应该使用列表、向量、哈希表或字典吗?谢谢你的评论 编辑:我已经创建了@soegaard建议的结构及其功能: (struct family (child father) #:tra

我有以下孩子及其父亲姓名的数据:

sally, THOMAS
alfred, JOHNSON
peter, SIMON
dick, THOMAS
harry, JOHNSON
eliz, SIMON
我想有一个函数,可以找到某个孩子的兄弟姐妹。例如(兄弟姐妹“哈里”)应该返回“阿尔弗雷德”。球拍的哪个结构最适合这个?我应该使用列表、向量、哈希表或字典吗?谢谢你的评论

编辑:我已经创建了@soegaard建议的结构及其功能:

(struct family (child father) #:transparent)
(define datalist (list  (family 'sally 'THOMAS) (family 'alfred 'JOHNSON) 
   (family 'peter 'SIMON) (family 'dick 'THOMAS) 
   (family 'harry 'JOHNSON) (family 'eliz 'SIMON)))

(define (siblings child_name lst)
  (define outl (for/list ((item lst)
    #:when (equal? (family-child item) child_name)) item))
  (for/list ((item lst)
    #:when (and (equal? (family-father item) (family-father (list-ref outl 0)))
                (not (equal? (family-child item) child_name))))
    (family-child item)  ))    

(siblings 'harry datalist)
它起作用了。输出为:

'(alfred)
有更好的办法吗

Edit2:按照@soegaard的建议使用findf(也可以映射和过滤,而不是for/list):

它的工作原理是:

'(alfred)

不是很漂亮,但只使用内置函数。作为一个数据结构,我使用了一个简单的对列表

(define (siblings x)
  (define tmp1
    (second (first (memf (lambda (arg)
                           (eq? (first arg) x))
                         lst))))
  (define tmp2 (filter (lambda (ar) (eq? (second ar) tmp1)) lst))
  (define tmp3 (filter (lambda (ar) (not (eq? (first ar) x))) tmp2))
  (map (lambda (ar) (first ar)) tmp3))
样本数据:

(define lst
  (list '("zosia" "Thomas")
        '("sally" "Thomas")
        '("sophie" "Thomas")
        '("magdalena" "Kazimierz")))
函数中的第一个define(tmp1)给出了x的父亲的名字,第二个filter对只留下那些父亲是x的父亲的,第三个用x消除了对,最后函数返回给定参数的兄弟姐妹列表

(define data '((sally THOMAS) (alfred JOHNSON) (peter SIMON) 
               (dick THOMAS) (harry JOHNSON) (eliz SIMON)))

(define (siblings child data)
  (define father (second (assq child data)))
  (map first
       (filter (lambda (e)
                 (and (eq? (second e) father)
                      (not (eq? (first e) child))))
               data)))
测试

> (siblings 'harry data)
'(alfred)
编辑1

如果在更新的问题中使用结构,则会变成:

(define (siblings child data)
  (define father (family-father (findf (lambda (e) (eq? (family-child e) child)) data)))
  (map family-child
       (filter (lambda (e)
                 (and (eq? (family-father e) father)
                      (not (eq? (family-child e) child))))
               data)))
编辑2

或者,如果你想要更吵闹的东西:

(define (siblings child data)
  (define father (family-father (findf (lambda (e) (eq? (family-child e) child)) data)))
  (for/fold ((res null)) ((e (in-list data)))
    (define this-child (family-child e))
    (if (and (eq? (family-father e) father) (not (eq? this-child child)))
        (cons this-child res)
        res)))

注意:在这种情况下,如果希望子名称以与初始数据相同的顺序出现,则必须反转结果。

使用结构。试试这个:

   (struct person (first last) #:transparent)
   (define john (person "John" "Doe"))
   john
   (person-first john)
   (person-last john)
您的解决方案为O(n),效果良好。我在测试中创建了大约300万个族对象,您的过程会在1到2秒内生成一个答案

正如我在评论中提到的,您可以使用散列将其设为O(1)。它支持任何数据结构并用作索引。该值可以是树中的节点。因为我很懒,所以我保留了你的结构并进行了两次查找。一个从孩子到家庭,一个从父亲到家庭名单

#!racket
(define +fathers+ 1000000)
(define +search-average+ (format "Son-1-of-Father-~a" (quotient +fathers+ 2)))
(define +sons+ 3)

(struct family (child father) #:transparent)
(define family-by-child (make-hash))
(define family-by-father (make-hash))
(define datalist
  ;; make a few son father relationships
  (foldl (λ (father relations)
           (let loop ((n +sons+) (acc relations))
             (if (zero? n)
                 acc
                 (loop (sub1 n)
                       (let* ((son (format "Son-~a-of-~a" n father))
                              (fam (family son father)))
                         ;; These two updates the two indexes to do O(1) lookup on both
                         (hash-set! family-by-child son fam)
                         (hash-update! family-by-father
                                       father
                                       (lambda (old) (cons fam old))
                                       '())
                         (cons fam acc))))))
         '()
         ;; make a list of fathers
         (let loop ((n +fathers+) (acc '()))
           (if (zero? n)
               acc
               (loop (sub1 n)
                     (cons (format "Father-~a" n) acc))))))


;; This uses over one second on my machine, grows linear with number of objects
;; note that you need to copy in siblings definition from your question
(time (siblings +search-average+ datalist))

;; My implementation using O(1) hash lookup 
(define (hsiblings child-name)
  (define f (hash-ref family-by-child child-name #f))
  (define families (hash-ref family-by-father (family-father f) '()))
  (map family-child (filter (λ (e) (not (eq? f e))) families)))

;; This produces immediate results no matter the size
(time (hsiblings +search-average+))

现在,您仍然可以通过使用不可变哈希保持它的功能。因为不可变哈希使用树,所以它变成了O(logn)

我应该列出(结构父(子父)#:透明)实例吗?有什么特殊的功能来搜索结构吗?在这里使结构透明有什么好处吗?是的,列出一个清单。有一些函数可以搜索列表中的元素。请参阅racket/list上的文档。使用
findf
这对数据集进行两次线性传递,因此它是O(n),n是子对象的数量。建立一个最佳的数据结构不是一件小事,因为你先上一棵树,然后下一棵树,而简单的树通常只做一个,但是你可以通过在树中保持关系(下一棵树)和一个基于哈希表中的子代查找父代的索引来完成这个过程。如果你把一个小国的所有家庭都填在你的名单上,你会注意到其中的差别。我看不出一个人怎么能只做一次传递,因为一个人必须先找到父亲的名字,然后再找到他的所有孩子。@mso我说了两次线性传递,但仍然是O(n)。
#!racket
(define +fathers+ 1000000)
(define +search-average+ (format "Son-1-of-Father-~a" (quotient +fathers+ 2)))
(define +sons+ 3)

(struct family (child father) #:transparent)
(define family-by-child (make-hash))
(define family-by-father (make-hash))
(define datalist
  ;; make a few son father relationships
  (foldl (λ (father relations)
           (let loop ((n +sons+) (acc relations))
             (if (zero? n)
                 acc
                 (loop (sub1 n)
                       (let* ((son (format "Son-~a-of-~a" n father))
                              (fam (family son father)))
                         ;; These two updates the two indexes to do O(1) lookup on both
                         (hash-set! family-by-child son fam)
                         (hash-update! family-by-father
                                       father
                                       (lambda (old) (cons fam old))
                                       '())
                         (cons fam acc))))))
         '()
         ;; make a list of fathers
         (let loop ((n +fathers+) (acc '()))
           (if (zero? n)
               acc
               (loop (sub1 n)
                     (cons (format "Father-~a" n) acc))))))


;; This uses over one second on my machine, grows linear with number of objects
;; note that you need to copy in siblings definition from your question
(time (siblings +search-average+ datalist))

;; My implementation using O(1) hash lookup 
(define (hsiblings child-name)
  (define f (hash-ref family-by-child child-name #f))
  (define families (hash-ref family-by-father (family-father f) '()))
  (map family-child (filter (λ (e) (not (eq? f e))) families)))

;; This produces immediate results no matter the size
(time (hsiblings +search-average+))