@@ -29,10 +29,10 @@ julia> @varname x[:, 1][1+1]
29
29
x[:,1][2]
30
30
```
31
31
"""
32
- struct VarName{sym, T}
32
+ struct VarName{sym, T<: Lens }
33
33
indexing:: T
34
34
35
- VarName {sym} (indexing= ()) where {sym} = new {sym,typeof(indexing)} (indexing)
35
+ VarName {sym} (indexing= IdentityLens ()) where {sym} = new {sym,typeof(indexing)} (indexing)
36
36
end
37
37
38
38
"""
@@ -48,7 +48,7 @@ julia> VarName(@varname(x[1][2:3]))
48
48
x
49
49
```
50
50
"""
51
- function VarName (vn:: VarName , indexing = ())
51
+ function VarName (vn:: VarName , indexing= IdentityLens ())
52
52
return VarName {getsym(vn)} (indexing)
53
53
end
54
54
@@ -94,7 +94,7 @@ Base.:(==)(x::VarName, y::VarName) = getsym(x) == getsym(y) && getindexing(x) ==
94
94
95
95
# Composition rules similar to the standard one for lenses, but we need a special
96
96
# one for the "empty" `VarName{..., Tuple{}}`.
97
- Base.:∘ (vn:: VarName{sym,Tuple{} } , lens:: Lens ) where {sym} = VarName {sym} (lens)
97
+ Base.:∘ (vn:: VarName{sym,<:IdentityLens } , lens:: Lens ) where {sym} = VarName {sym} (lens)
98
98
Base.:∘ (vn:: VarName{sym,<:Lens} , lens:: Lens ) where {sym} = VarName {sym} (vn. indexing ∘ lens)
99
99
100
100
function Base. show (io:: IO , vn:: VarName{<:Any, <:Tuple} )
@@ -217,34 +217,14 @@ function subsumes(u::VarName, v::VarName)
217
217
return getsym (u) == getsym (v) && subsumes (u. indexing, v. indexing)
218
218
end
219
219
220
- subsumes (:: Tuple{} , :: Tuple{} ) = true # x subsumes x
221
- subsumes (:: Tuple{} , :: Tuple ) = true # x subsumes x[1]
222
- subsumes (:: Tuple , :: Tuple{} ) = false # x[1] does not subsume x
223
- function subsumes (t:: Tuple , u:: Tuple ) # does x[i]... subsume x[j]...?
224
- return _issubindex (first (t), first (u)) && subsumes (Base. tail (t), Base. tail (u))
225
- end
226
-
227
- const AnyIndex = Union{Int, AbstractVector{Int}, Colon}
228
- _issubindex_ (:: Tuple{Vararg{AnyIndex}} , :: Tuple{Vararg{AnyIndex}} ) = false
229
- function _issubindex (t:: NTuple{N, AnyIndex} , u:: NTuple{N, AnyIndex} ) where {N}
230
- return all (_issubrange (j, i) for (i, j) in zip (t, u))
231
- end
232
-
233
- const ConcreteIndex = Union{Int, AbstractVector{Int}} # this include all kinds of ranges
234
-
235
- """ Determine whether indices `i` are contained in `j`, treating `:` as universal set."""
236
- _issubrange (i:: ConcreteIndex , j:: ConcreteIndex ) = issubset (i, j)
237
- _issubrange (i:: Union{ConcreteIndex, Colon} , j:: Colon ) = true
238
- _issubrange (i:: Colon , j:: ConcreteIndex ) = true
239
-
240
- # E.g. `x`, `x[1]`, i.e. `u` is always subsumed by `t`
241
- subsumes (t:: Tuple{} , u:: Lens ) = true
242
- subsumes (t:: Lens , u:: Tuple{} ) = false
243
-
244
220
# Idea behind `subsumes` for `Lens` is that we traverse the two lenses in parallel,
245
221
# checking `subsumes` for every level. This for example means that if we are comparing
246
222
# `PropertyLens{:a}` and `PropertyLens{:b}` we immediately know that they do not subsume
247
223
# each other since at the same level/depth they access different properties.
224
+ # E.g. `x`, `x[1]`, i.e. `u` is always subsumed by `t`
225
+ subsumes (t:: IdentityLens , u:: Lens ) = true
226
+ subsumes (t:: Lens , u:: IdentityLens ) = false
227
+
248
228
subsumes (t:: ComposedLens , u:: ComposedLens ) = subsumes (t. outer, u. outer) && subsumes (t. inner, u. inner)
249
229
250
230
# If `t` is still a composed lens, then there is no way it can subsume `u` since `u` is a
@@ -273,39 +253,114 @@ subsumes(t::ComposedLens{<:IndexLens}, u::IndexLens) = subsumes_index(t, u)
273
253
# the indexing behavior must be considered jointly.
274
254
# Therefore we must recurse until we reach something that is NOT
275
255
# indexing, and then consider the sequence of indices leading up to this.
276
- function subsumes_index (t, u)
256
+ """
257
+ subsumes_index(t::Lens, u::Lens)
258
+
259
+ Return `true` if the indexing represented by `t` subsumes `u`.
260
+
261
+ This is mostly useful for comparing compositions involving `IndexLens`
262
+ e.g. `_[1][2].a[2]` and `_[1][2].a`. In such a scenario we do the following:
263
+ 1. Combine `[1][2]` into a `Tuple` of indices using [`combine_indices`](@ref).
264
+ 2. Do the same for `[1][2]`.
265
+ 3. Compare the two tuples from (1) and (2) using `subsumes_index`.
266
+ 4. Since we're still undecided, we call `subsume(@lens(_.a[2]), @lens(_.a))`
267
+ which then returns `false`.
268
+
269
+ # Example
270
+ ```jldoctest; setup=:(using Setfield)
271
+ julia> t = @lens(_[1].a); u = @lens(_[1]);
272
+
273
+ julia> subsumes_index(t, u)
274
+ false
275
+
276
+ julia> subsumes_index(u, t)
277
+ true
278
+
279
+ julia> # `IdentityLens` subsumes all.
280
+ subsumes_index(@lens(_), t)
281
+ true
282
+
283
+ julia> # None subsumes `IdentityLens`.
284
+ subsumes_index(t, @lens(_))
285
+ false
286
+
287
+ julia> AbstractPPL.subsumes(@lens(_[1][2].a[2]), @lens(_[1][2].a))
288
+ false
289
+
290
+ julia> AbstractPPL.subsumes(@lens(_[1][2].a), @lens(_[1][2].a[2]))
291
+ true
292
+ ```
293
+ """
294
+ function subsumes_index (t:: Lens , u:: Lens )
277
295
t_indices, t_next = combine_indices (t)
278
296
u_indices, u_next = combine_indices (u)
279
297
280
- # Check if the indices indicate that `t` subsumes `u` .
281
- if ! subsumes (t_indices, u_indices)
298
+ # If we already know that `u` is not subsumed by `t`, return early .
299
+ if ! subsumes_index (t_indices, u_indices)
282
300
return false
283
301
end
284
302
285
303
if t_next === nothing
286
304
# Means that there's nothing left for `t` and either nothing
287
305
# or something left for `u`, i.e. `t` indeed `subsumes` `u`.
288
306
return true
289
- else
290
- # `t` only `subsumes` `u` if `u_next` is also nothing.
291
- if u_next === nothing
292
- return true
293
- else
294
- return false
295
- end
307
+ elseif u_next === nothing
308
+ # If `t_next` is not `nothing` but `u_ntext` is, then
309
+ # `t` does not subsume `u`.
310
+ return false
296
311
end
297
312
298
- # If neither is `nothing` we continue iterating .
313
+ # If neither is `nothing` we continue.
299
314
return subsumes (t_next, u_next)
300
315
end
301
316
317
+ """
318
+ combine_indices(lens)
319
+
320
+ Return sequential indexing into a single `Tuple` of indices,
321
+ e.g. `x[:][1][2]` becomes `((Colon(), ), (1, ), (2, ))`.
322
+
323
+ The result is compatible with [`subsumes_index`](@ref) for `Tuple` input.
324
+ """
302
325
combine_indices (lens:: Lens ) = (), lens
303
326
combine_indices (lens:: IndexLens ) = (lens. indices, ), nothing
304
327
function combine_indices (lens:: ComposedLens{<:IndexLens} )
305
328
indices, next = combine_indices (lens. inner)
306
329
return (lens. outer. indices, indices... ), next
307
330
end
308
331
332
+ """
333
+ subsumes_index(left_index::Tuple, right_index::Tuple)
334
+
335
+ Return `true` if `right_index` is subsumed by `left_index`.
336
+
337
+ Currently _not_ supported are:
338
+ - Boolean indexing, literal `CartesianIndex` (these could be added, though)
339
+ - Linear indexing of multidimensional arrays: `x[4]` does not subsume `x[2, 2]` for a matrix `x`
340
+ - Trailing ones: `x[2, 1]` does not subsume `x[2]` for a vector `x`
341
+ - Dynamic indexing, e.g. `x[1]` does not subsume `x[begin]`.
342
+ """
343
+ subsumes_index (:: Tuple{} , :: Tuple{} ) = true # x subsumes x
344
+ subsumes_index (:: Tuple{} , :: Tuple ) = true # x subsumes x[1]
345
+ subsumes_index (:: Tuple , :: Tuple{} ) = false # x[1] does not subsume x
346
+ function subsumes_index (t:: Tuple , u:: Tuple ) # does x[i]... subsume x[j]...?
347
+ return _issubindex (first (t), first (u)) && subsumes_index (Base. tail (t), Base. tail (u))
348
+ end
349
+
350
+ const AnyIndex = Union{Int, AbstractVector{Int}, Colon}
351
+ _issubindex_ (:: Tuple{Vararg{AnyIndex}} , :: Tuple{Vararg{AnyIndex}} ) = false
352
+ function _issubindex (t:: NTuple{N, AnyIndex} , u:: NTuple{N, AnyIndex} ) where {N}
353
+ return all (_issubrange (j, i) for (i, j) in zip (t, u))
354
+ end
355
+
356
+ const ConcreteIndex = Union{Int, AbstractVector{Int}} # this include all kinds of ranges
357
+
358
+ """ Determine whether indices `i` are contained in `j`, treating `:` as universal set."""
359
+ _issubrange (i:: ConcreteIndex , j:: ConcreteIndex ) = issubset (i, j)
360
+ _issubrange (i:: Colon , j:: Colon ) = true
361
+ _issubrange (i:: ConcreteIndex , j:: Colon ) = false
362
+ _issubrange (i:: Colon , j:: ConcreteIndex ) = true
363
+
309
364
"""
310
365
concretize(l::Lens, x)
311
366
@@ -471,70 +526,3 @@ function vsym(expr::Expr)
471
526
end
472
527
end
473
528
474
- """
475
- @vinds(expr)
476
-
477
- Returns a tuple of tuples of the indices in `expr`.
478
-
479
- ## Examples
480
-
481
- ```jldoctest
482
- julia> @vinds x
483
- ()
484
-
485
- julia> @vinds x[1,1][2,3]
486
- ((1, 1), (2, 3))
487
-
488
- julia> @vinds x[:,1][2,:]
489
- ((Colon(), 1), (2, Colon()))
490
-
491
- julia> @vinds x[2:3,1][2,1:2]
492
- ((2:3, 1), (2, 1:2))
493
-
494
- julia> @vinds x[2:3,2:3][[1,2],[1,2]]
495
- ((2:3, 2:3), ([1, 2], [1, 2]))
496
- ```
497
-
498
- !!! compat "Julia 1.5"
499
- Using `begin` in an indexing expression to refer to the first index requires at least
500
- Julia 1.5.
501
- """
502
- macro vinds (expr:: Union{Expr, Symbol} )
503
- return vinds (expr)
504
- end
505
-
506
-
507
- """
508
- vinds(expr)
509
-
510
- Return the indexing part of the [`@varname`](@ref)-compatible expression `expr` as an expression
511
- suitable for input of the [`VarName`](@ref) constructor.
512
-
513
- ## Examples
514
-
515
- ```jldoctest
516
- julia> vinds(:(x[end]))
517
- :((((lastindex)(x),),))
518
-
519
- julia> vinds(:(x[1, end]))
520
- :(((1, (lastindex)(x, 2)),))
521
- ```
522
- """
523
- function vinds end
524
-
525
- vinds (expr:: Symbol ) = Expr (:tuple )
526
- function vinds (expr:: Expr )
527
- if Meta. isexpr (expr, :ref )
528
- ex = copy (expr)
529
- @static if VERSION < v " 1.5.0-DEV.666"
530
- Base. replace_ref_end! (ex)
531
- else
532
- Base. replace_ref_begin_end! (ex)
533
- end
534
- last = Expr (:tuple , ex. args[2 : end ]. .. )
535
- init = vinds (ex. args[1 ]). args
536
- return Expr (:tuple , init... , last)
537
- else
538
- error (" Mis-formed variable name $(expr) !" )
539
- end
540
- end
0 commit comments