Findfirst that returns the value/element


#1

Sorry for the maybe very trivial question but I couldn’t find it anywhere in the documentation.
Is there a function that does this:
filter(func , iterable)[1]?


#2

https://docs.julialang.org/en/stable/stdlib/arrays/#Base.findfirst-Tuple{Function,Any}


#3

This doesn’t return the element, only index as far as I understand?


#4

Then you can retrieve the element using getindex ([]). If that is costly for some reason, you can roll your own very easily:

function findfirst_and_elt(pred, itr)
    for (index, elt) in enumerate(itr)
        if pred(elt)
            return index, elt
        end
    end
    nothing
end

x = 'a' .+ (0:9)

findfirst_and_elt(z -> z == 'f', x)

#5

Yes, sure, it just seemed that since it is such a trivial extension and (maybe) used a lot, there would be a built in function, and that I just couldn’t locate it. But so there seems to be none. Okay thanks!


#6

The problem ist the case where for no element the predicate is satisfied. What do you return then? With the optimizations in 0.7 one could return nothing, but previously this would have been a major performance hit.


#7

There are zillion variations on these functions, and not each of them can end up in Base. The best would be supporting a vocabulary which can be combined to obtain this, but I have no good suggestion ATM.


#8

While cleaning up the search and find functions, we realized we already have findmin and findmax which return both the index and the value. So it could make sense to add variants of the find* functions doing that, but that will probably won’t happen for 0.7/1.0. We also haven’t been able to find a good name for functions following this pattern. See this issue.


#9

Yes this was exactly why I thought there would be something! I don’t directly know a solution to the naming. I think for my own I’ll call it firstval or findfirstval.

I think the same idea as you would get for a findfirst with no solutions, nothing seems to make sense in that regard.


#10

The problem is type instability. If you get back the index, you can check whether it is inbounds in order to see whether you have a first value. If the API instead returned e.g. a tuple (idx_first, val_first) then this is type-unstable (either Tuple{Int64, eltype(V)} or Tuple{Int64, Void}). Even worse, if val_first is not bitstype, then this might allocate.

Hence, the state of the compiler constrains API design; I am very happy that the base API is mostly designed in a way that encourages people to write code that compiles well. In fact, I think julia does not go far enough in this direction (convenience functions that cannot be compiled fast should not be in base, imho; e.g. enumerate should not exist in base until it can be guaranteed to not allocate).


#11

To be fair, multiple functions on 0.7 are type unstable, for example findfirst.

Guess we have to remove tuples from Base.


#12

Sure; and I guess that the 0.6 compiler not being as good at producing fast code for Union{T,Void} is the reason for it not being type unstable for arrays before.

Sorry, I did not want to sound disparaging here. I am not advocating removal of anything in Base; also, tuples are sometimes unavoidable. Just saying that enumerate in 0.6 is very dangerous for new users and had very limited upsides (upside: save up to two lines of code; downside: bad performance when not bitstype; one can (always?) write for (i,v) in enumerate(X) body end as i=0; for v in X i += 1; body end.). This does not mean that I argue for a removal of enumerate, but I would have argued against its introduction if I had been around back then. Any enumerate is a landmine waiting for unsuspecting users to feed a surprise non-bitstype, and realistically these landmines will only get defused through compiler updates.


#13

I agree on the notion that code inside Base should be type stable, I actually didn’t know this was not true. I think that I also maybe wouldn’t mind less functionality if all if the built in functionality gives you type stability. The point is that when I have code that is a lot slower than I would expect, I would probably never think that it is coming from a built-in function.

On the whole Void thing, do I understand correctly that the new Some type is the answer to this? Is it like a Void that is type specific?


#14

I kind of like getfirst.

findfirst finds the location of the first element, and getfirst retrives it. It also reminds me of getindex, getfield, etc. that also retrieve values.


#15

Julia allows programmers to write very efficient code, for which type stability is helpful, but this does not mean that the user is forced to do this all the time. That would be very inconvenient. Expressive constructs have their place even when not optimized to squeeze out the last bit of performance.


#16

I totally agree. It’s just that the advantage of enumerate in expressiveness is so trivial that it doesn’t justify the landmine, imho; but that ship has sailed anyway.


#17

No, Some{T} is just a very simple type which wraps a value of type T to allow distinguishing Some(nothing) from nothing. It has nothing special, and is mostly useful with Union{Some{T}, Nothing}.