Racket中对的结构

时间:2016-08-17 10:56:45

标签: scheme racket

我有以下父亲姓名的儿童数据:

-assets
-commands
-components
-config
  -db.php
  -params.php
-controllers
-models
-modules
  -queue
    -controllers
    -models
      -Queues.php
    -views
    Queue.php
-runtime

我希望有一个功能可以找到特定孩子的兄弟姐妹。例如(兄弟“哈利”)应该返回“阿尔弗雷德”。 Racket中哪种结构最适合这种情况?我应该使用列表,向量或哈希表还是字典?感谢您的意见。

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

sally, THOMAS
alfred, JOHNSON
peter, SIMON
dick, THOMAS
harry, JOHNSON
eliz, SIMON

有效。输出是:

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

有更好的方法吗?

Edit2:使用@soegaard建议的findf(也是map和filter而不是for / list):

'(alfred)

有效:

(define (siblings child_name lst)
  (define fam (findf (lambda (x) (equal? (family-child x) child_name)) lst ))
  (map (lambda (x) (family-child x))
       (filter (lambda (x) (and (equal? (family-father x) (family-father fam))
                                (not (equal? (family-child x) child_name))))
               lst)) )

(siblings 'harry datalist)

4 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:2)

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

(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")))

函数中的第一个定义(tmp1)给出了x父亲的名字,第二个过滤器对,只留下父亲是x父亲的那些,第三个用x替换了对, finally函数返回给定参数的兄弟列表。

答案 2 :(得分:1)

您的解决方案是O(n)并且效果很好。我在测试中创建了大约300万个家庭对象,并且您的程序在一到两秒之间产生答案。

正如我在评论中提到的,你可以通过使用哈希来使其成为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(log n),因为不可变哈希值使用树。

答案 3 :(得分:0)

使用结构。试试这个:

   (struct person (first last) #:transparent)
   (define john (person "John" "Doe"))
   john
   (person-first john)
   (person-last john)