-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
missing each/map (¨) #69
Comments
I fancy having a go at implementing this one myself, but was curious if there was an obvious reason it was left out. |
I've wanted it too. Let me tackle it, although it's a bit tricky. It might require a long-desired feature of having operators be first class, although special parser support might be enough. In the meantime you can write a loop for a specific case, but you'd need a different loop for each operator. |
Brilliant, thanks (I suspected it wasn't going to be as simple as adding another style of / or .).
By way of an experience report, I hit similar issues to as @rsc discussed in his recent PR when writing loops over matrices, where it was sometimes hard to work out the terminal conditions reliably. |
The core issue is that there is nothing in ivy corresponding to what are called operators in APL (ivy's operators are functions in APL speak). The "each" token represents an operator, which is something that modifies the behavior of a function. I spent some time on this today and found a couple of ways to hack it in, but nothing that I'm happy with yet. As I said, I might go back to my original (and also not easy; I didn't plan for this) idea of allowing operators to be values themselves. Then an operator could also be an operand, which has many benefits (including allowing anonymous blocks of code to be operators). |
Thanks for the elaboration. Reading around it seems that /, \ etc are also operators (and o. is a special cased one), all very interesting! Sounds like there a good argument for skipping later APL's approaches, and doing something more regular like J's approach (talking way beyond what I understand here). |
I've been thinking a lot about this. I agree that something's missing, and also that APL's each is not exactly right as is. One thing I was considering is saying |
I forgot the second half of what I'd been thinking with @. It could be made to work on the other side, too, so that
or if you had something that needed vectors on both sides then
would be given by x[i] = z[i] f z[i] It's a little weird looking. Another thing I've been wondering is whether there's any way to say what the maximum rank of an argument is, a kind of optional type in the operator definition. If you could define something like |
This is pretty much what I've had in mind. The doubling action of the modifier is a nice wrinkle. I hadn't come up with a character yet, though. Not sure I like @ but that's what bikeshedding is for. |
I implemented, by analogy with o., map., to iterate over the elements of the right hand side. So map.! 1 2 3 prints 1 2 6 It was easy to do but it's not useful. Almost all the unary operators already apply elementwise, including simple user-defined ones. The few that don't, such as rho and iota, produce results of varying sizes, and it's not easy to see how the results should pack given ivy's data model. map.iota 1 2 3 would produce the integers 1 1 2 1 2 3 but not as a simple vector of scalars, but as 1 (1 2) (1 2 3), and ivy's data model does not allow compound elements in vectors, so this is an error. So I think simple map is out. Dyalog's each (¨) works, but dyalog has a richer data model. The rhs of tryapl.org's example of its use is illegal in ivy. Still thinking about rsc's idea. |
In this example you could define a unary operator:
(some more complex functions require map/each as described in the rest of the thread) |
I implemented @ in my copy of my Ivy, but I haven't sent it out. The implementation needs work and I don't know if it's the right idea anyway. But it has one property that seems pretty nice. I was wrong about what After using this for a few days (for example, here in this video) I find it much easier to think of the outer product this way. I never really got used to Rob's comment about map not being needed for the basic operators is absolutely true: they all automatically extend to vectors well already. It is far more useful for user-defined operators, which are often defined in ways that don't automatically extend to vectors (for example, later in the video). One basic operator that does benefit is transp. If you have, say, a 5 16 16 called x, interpreted as 5 16x16 matrices and you want to transpose them all, you can (now) do 1 3 2 transp x, but transp@ x is a bit clearer. |
I pushed my @ implementation in #83. It's not for review, just for discussion. One possible alternative spelling would be "e." and ".e" instead of "@", |
I admit a purely subjective dislike for the notation '@', but do like what your functor can achieve. The letter 'e' seems an improvement to me, by analogy with 'o', but then 'o' didn't intentionally stand for "outer" but rather as an analog for the APL ∘ functor. As a result, to me, although 'e' is short, 'each' says more, and reads well: x each.dist.each y Although that is more verbose, it reads clearly. We'd probably get used to the single letter in time, though. Analogy with dyalog is probably not worth pursuing, as I shied away (perhaps for simplicity, perhaps for timidity) of having elements of matrices and vectors be anything other than scalars. So I'm leaning towards following your lead, but must also admit I'm not fully conversant with it yet. |
What if we follow the
because |
@rsc, I have rebased your demo branch onto master to try it out it with the latest Ivy features. I'm trying to make use of
I'm curious if it's feasible to use the |
Ok, I figured it out myself:
|
I was looking for an "each" for a user defined operation and added the
It does seem like it could be adding complexity and it might be better to make user defined operations first-class. PR: superfrink#1 |
It might be that I'm missing something, but mapping an operator over a vector is super useful and I had to jump through some hoops to do it.
Given a vector, compute the triangle number for each element (but any function would apply). I ended up defining the function as an operator that ignored one of its args. then reducing over a vector of pairs...
Is a map / each syntactically difficult to support though, feels like it should be similar to reduce?
The text was updated successfully, but these errors were encountered: