2015-11-06 7 views
4

В R2 и R3, можно использовать unique для удаления повторяющихся элементов из серии:Удалить дубликаты объектов из серии в Rebol

>> a: [1 2 2 3] 
>> length? a 
== 4 
>> length? unique a 
== 3 

Как я могу выполнять ту же операцию на ряде объектов? например,

b: reduce [ 
    make object! [a: 1] 
    make object! [b: 2] 
    make object! [b: 2] 
    make object! [c: 3] 
] 
>> length? b 
== 4 
>> length? unique b 
== 4 ; (I'd like it to be 3) 

ответ

3

Осуществление проверки равенства в UNIQUE и других операций набора, как представляется, Cmp_Value, и способ сравнения делается, чтобы вычесть кадров указатели объектов. Если это вычитание равно нулю (например, это тот же самый объект?), То сравнение считается матч:

f-series.c Line 283, R3-Alpha open source release

Если вы посмотрите на окружающий код, который вы увидите вызов Cmp_Block в той же процедуре , В случае Cmp_Block это делает рекурсивное сравнение, и чтит чувствительность к регистру ... поэтому разница между тем, как блоки и объекты действуют:

Cmp_Block() in f-series.c

Учитывая, что написано, что путь, если вы хотите, UNIQUE-операция, основанная на сравнении полей между объектами и их идентичностью, нет возможности сделать это, помимо написания вашей собственной процедуры и вызова EQUAL? ... или изменения кода C.

Вот короткий взлом, не требующий изменения источника C, который делает MAP-EACH на выходе UNIQUE. Тело отфильтровывает любой EQUAL? объекты, которые уже были замечены (потому что, когда тело МАР-КАЖДОЙ возвращаются неустановленной, он ничего не добавляет к результату):

my-unique: function [array [block!]] [ 
    objs: copy [] 
    map-each item unique array [ 
     if object? :item [ 
      foreach obj objs [ 
       if equal? item obj [unset 'item break] 
      ] 
      unless unset? :item [append objs item] 
     ] 
     :item ;-- if unset, map-each adds nothing to result 
    ] 
] 

К сожалению, вы должны использовать BLOCK! а не MAP! отслеживать объекты, как вы идете, потому что MAP! в настоящее время не разрешает объекты как ключи. Если бы они допустили это, у них, вероятно, была бы такая же проблема, что и объекты с равными равными единицами хэширования.

. (Примечание: Фиксация это и другие вопросы на радаре ветви Рен-C, который, в дополнение к в настоящее время самый быстрый Rebol интерпретатор с fundamental fixes, также имеет немного enhancements to the set operations обсуждения в chat)

+0

Просто хотел добавить примечание, что это только R3, но там отлично работает. Спасибо за ответ! –

2

EQUAL? и SAME? возвращает true для объектов, только если они являются одними и теми же объектами. Я написал функцию, чтобы проверить сходство между объектами, он возвращает истину, если два объекта имеют одинаковые слова с одинаковыми значениями и тех же типов:

similar?: func [ 
    {Returns true if both object has same words in same types.} 
    o [object!] p [object!] /local test 
][ 
    test: [if not equal? type? get in o word type? get in p word [return false]] 
    foreach word sort first o test 
    foreach word sort first p test 
    true 
] 

Вы можете проверить следующим образом:

>> o: make object! [b: 2] 
>> p: make object! [b: 2] 
>> equal? o p 
== false 
>> same? o p 
== false 
>> similar? o p 
== true 

Вы можете использовать это в вашем случае.

2
unique+: func [array [block!]] [ 
    objs: copy [] 
    map-each item unique array [ 
     if object? :item [ 
      foreach obj objs [ 
       if equal-object? :item :obj [unset 'item break] 
      ] 
      if value? 'item [append objs item] 
     ] 
     ;-- If unset, map-each adds nothing to result under R3. 
     ; R2 behaves differently. This works for both. 
     either value? 'item [:item] [()] 
    ] 
] 

equal-object?: func [ 
    "Returns true if both objects have same words and values." 
    o [object!] p [object!] 
][ 
    if not equal? sort words-of o sort words-of p [return false] 
    foreach word words-of o [ 
     if not equal? o/:word p/:word [return false] 
    ] 
    true 
] 
Смежные вопросы