Удалить повторяющиеся объекты из серий в Rebol

В R2 и R3 я могу использовать unique для удаления повторяющихся элементов из серии:

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

person James Irwin    schedule 06.11.2015    source источник


Ответы (2)


Реализация проверки на равенство в UNIQUE и других операциях над множествами выглядит как Cmp_Value, и способ сравнения заключается в вычитании указателей фреймов объектов. Если это вычитание равно нулю (например, это ОДИНАКОВЫЙ объект?), тогда сравнение считается совпадением:

f-series.c Строка 283, R3- Альфа-версия с открытым исходным кодом

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

Cmp_Block() в f-series.c< /а>

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

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

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
    ]
]

К сожалению, вы должны использовать БЛОК! а не МАП! следить за объектами, когда вы идете, потому что КАРТА! в настоящее время не позволяет использовать объекты в качестве ключей. Если бы они разрешили это, у них, вероятно, возникла бы та же проблема, связанная с неодинаковым хэшированием одинаковых по полю объектов.

(Примечание. Исправление этой и других проблем находится в поле зрения ветки Ren-C, которая, помимо того, что теперь является самым быстрым интерпретатором Rebol с фундаментальные исправления, а также небольшие улучшения в операциях set. Обсуждения в чате)

person HostileFork says dont trust SE    schedule 06.11.2015
comment
Просто хотел добавить примечание, что это только для R3, но там отлично работает. Спасибо за ответ! - person James Irwin; 11.11.2015

РАВНЫЙ? и ЖЕ? возвращает true для объектов, только если они являются одними и теми же ссылками на объекты. Я написал функцию для проверки сходства между объектами, она возвращает 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

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

person endo64    schedule 09.11.2015