cleoold / types-linq

Standard sequence helper algorithms with full typing support
BSD 2-Clause "Simplified" License
15 stars 1 forks source link

except1方法能否支持不可哈希类型? #6

Closed Yxa2111 closed 3 years ago

Yxa2111 commented 3 years ago

except1方法在元素类型是不可哈希类型的时候就会抛异常,比如当类型为list的时候:

lst = [[1, 2, 3], [4, 5], [6, 7]]
spec_lst = [[4], [1, 2]]
print(Enumerable(lst).select(lambda lst: lst[:-1]).except1(spec_lst).to_list())
# 过滤掉前两个list

会抛异常:

  File "D:\Program Files\Python39\lib\site-packages\types_linq\enumerable.py", line 826, in to_list
    return [e for e in self]
  File "D:\Program Files\Python39\lib\site-packages\types_linq\enumerable.py", line 826, in <listcomp>
    return [e for e in self]
  File "D:\Program Files\Python39\lib\site-packages\types_linq\enumerable.py", line 302, in inner
    s = {*second}
TypeError: unhashable type: 'list'

所以只能用where去实现:

lst = [[1, 2, 3], [4, 5], [6, 7]]
spec_lst = [[4], [1, 2]]
print(Enumerable(lst).select(lambda lst: lst[:-1]).where(lambda l: l not in spec_lst).to_list())
# 过滤掉前两个list
# output: [[6]]

看了下实现是先将second插入到hashset里,所以能否对于对这样的类型也提供支持?比如先判断second元素是否可哈希,否则就放进list或者之类的OrderedSet(list可比较)?

cleoold commented 3 years ago

誒。如果要支持的話,對於這種 unhashale 類型可能有兩種實現集合操作的方法:1. 比較 id (用 is),2. 比較内容 (==)。在 .NET 好像是用前者來著。

如果使用樓主説的方法(後者),這時候效率就有可能變成 O(nm),不過語義可能更明顯些。

我不知道該怎麽弄(這也是 py 的限制 - set 不能傳遞自定義的 == 和 hash())。有實際使用 except unhashable 類型的例子嗎?

cleoold commented 3 years ago

BTW,根據 .net 你給的兩條實際上是不完全相同的

In [3]: lst = [(1, 2, 3), (4, 5), (6, 7), (6, 7)]

In [4]: spec_lst = [(4,), (1, 2)]

In [5]: Enumerable(lst).select(lambda lst: lst[:-1]).except1(spec_lst).to_list()
Out[5]: [(6,)]

In [6]: Enumerable(lst).select(lambda lst: lst[:-1]).where(lambda l: l not in spec_lst).to_list()
Out[6]: [(6,), (6,)]

(tuple is hashable)

Yxa2111 commented 3 years ago

我忘记重复的情况了,所以大概 where 后面再加个 distinct() 才能等价于 except1() ?

我觉得应该是比较值吧,实现的话能不能先试下放到 set 里,如果抛异常了就放到 list 里?然后用 in operator 看下是否在 list 里,不是的话就 append;或者放到平衡树 set 里?不过似乎python 好像没有内置的平衡树 set ...

cleoold commented 3 years ago

我忘记重复的情况了,所以大概 where 后面再加个 distinct() 才能等价于 except1() ?

我觉得应该是比较值吧,实现的话能不能先试下放到 set 里,如果抛异常了就放到 list 里?然后用 in operator 看下是否在 list 里,不是的话就 append;或者放到平衡树 set 里?不过似乎python 好像没有内置的平衡树 set ...

如果要改動這個地方,其餘的例如 .union(),.intersect(), LookUp 用到 set[V] 和 dict[K, V] 的地方都要改動來支持不可哈希的鍵。所以要把用到這些東西的地方都換成你所説的方法。

需要定義新的 MutableSet[V] 和 MutableMapping[K, V] 放在一個 util 模塊裏。大佬願意貢獻嗎?