Using zip() with count()

I’m trying to use count where the iterator is taken from zip.

Here is the code I’ve written:

function hamming(c, d)
    count((x,y) -> (x != y), zip(c,d))
end

However, when this is run on any non-empty collections, e.g. hamming("123","1X3"), an exception is thrown:

ERROR: MethodError: no method matching (::getfield(Main, Symbol(“##9#10”)))(::Tuple{Char,Char})

Closest candidates are:

#9(::Any, ::Any) at REPL[71]:2

Stacktrace:

[1] count( ::getfield(Main, Symbol(“##9#10”)), ::Base.Iterators.Zip{Tuple{String,String}} ) at ./reduce.jl:772

[2] hamming( ::String, ::String ) at ./REPL[71]:2

[3] top-level scope at REPL[72]:1

I can’t see any obvious reason this wouldn’t work based on the documentation for count().

There is also no problem when count is replaced by an equivalent for loop, something like:

for (x,y) in zip("123","1X3")
    print(x == y ? 0 : 1)
end

Can someone help me out? What have I misunderstood?

FYI:
https://github.com/JuliaStats/Distances.jl/blob/7f3a28c0d1372e3b3edbcbc28f00ba5645e1bbdb/src/metrics.jl#L332


Regarding your question:
I noticed the following would work:

julia> count(x -> (x[1] == x[2]), zip("1X3","123"))
2
2 Likes

The result of zip is a tuple. How about this?

function hamming(c, d)
    count(t -> (t[1] == t[2]), zip(c,d))
end
4 Likes

count( ( (x,y)::Tuple{Any,Any} )->(x==y),zip("123","1X3"))

I think

(x,y) -> (x == y)

is interpreted as a function with two parameters x::Any and y::Any and not as a single Tuple parameter

1 Like

As others have pointed out, zip produces Tuples, as a single argument. You can also do

hamming(c, d) = count(Base.splat(==), zip(c,d))
4 Likes

Very nice!

As an alternative, similar to oheil’s solution but imho more canonical, I’d suggest using an anonymous function with one argument used with tuple destructuring.
So you can write:

hamming(c,d) = count( ((x,y),) -> x == y, zip(c,d))

where we have a anonymous function ((x,y),) -> x == y which has one argument (note that the one-argument tuple is (a,) not (a) - in the latter case the parens are interpreted as showing precedence which doesn’t make sense here.
This one argument is then destructured as a tuple of two elements.

This works in general and doesn’t rely on unexported Base functions.

2 Likes

Perfect, all makes sense - thanks everyone!

It didn’t occur to me that (x,y) -> x == y would be interpreted as a function of two parameters, whereas I was aiming for a function of one (tuple) parameter.

I also just noticed I supplied the wrong condition in the first post for the Hamming distance.

Original: count(x -> x[1] == x[2], zip(s1,s2))
Fixed: count(x -> x[1] != x[2], zip(s1,s2))

Apologies to everyone whose replies are now slightly wrong.

3 Likes