Skip to content

Commit

Permalink
change iteration over GAP objects (#1057)
Browse files Browse the repository at this point in the history
* change iteration over GAP objects

- omit holes when iterating over GAP lists
- extend documentation
- fix a GAPDoc error in the JuliaInterface manual

* declare a missing local variable in GAP

The effect of this missing declaration was strange:
I did not get error messages when trying `include("test/runtests.jl")`,
but the CI tests showed the GAP warning
together with an (intended) error message,
which caused a different output there and hence a test failure.
  • Loading branch information
ThomasBreuer authored Oct 21, 2024
1 parent 7d1ab87 commit 5cbad84
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 3 deletions.
60 changes: 60 additions & 0 deletions docs/src/other.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,66 @@ in order to support special GAP syntax beyond function calls with arguments.
- Access components of a component object via [`getbangproperty`](@ref),
equivalent to GAP's `!.` operator.

- Iterate over a GAP list or collection (such as a GAP domain) `obj`
via `for x in obj`,
or `map(f, obj)` for a unary function `f`,
or `[f(x) for x in obj]`.
If `obj` is not a list then a GAP iterator for `obj` gets constructed.

```jldoctest
julia> l = [1, 2, 3]; gl = GapObj(l)
GAP: [ 1, 2, 3 ]
julia> ll = []; for x in gl push!(ll, x); end; ll == l
true
julia> map(x -> x, gl) == l
true
julia> [x for x in gl] == l
true
```

- Iterating in Julia over a GAP list `obj` skips the unbound entries in `obj`,
like iterating in GAP does.

```jldoctest
julia> gl = GAP.evalstr("[1,, 3]")
GAP: [ 1,, 3 ]
julia> ll = []; for x in gl push!(ll, x); end; ll
2-element Vector{Any}:
1
3
```

- Note that iterating in Julia over a GAP iterator object `obj` does *not*
change `obj`, whereas iterating in GAP over `obj` changes `obj`.

```jldoctest
julia> g = GAP.Globals.SymmetricGroup(3)
GAP: Sym( [ 1 .. 3 ] )
julia> iter = GAP.Globals.Iterator(g)
GAP: <iterator>
julia> [x for x in iter] == collect(g)
true
julia> [x for x in iter] == collect(g)
true
julia> f = GAP.evalstr("function(itr) local res, i; res:= [];" *
"for i in itr do Add( res, i ); od; return res; end")
GAP: function( itr ) ... end
julia> f( iter )
GAP: [ (), (2,3), (1,3), (1,3,2), (1,2,3), (1,2) ]
julia> f( iter )
GAP: [ ]
```

```@docs
call_gap_func
call_with_catch
Expand Down
2 changes: 1 addition & 1 deletion pkg/JuliaInterface/gap/JuliaInterface.gd
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ DeclareGlobalFunction( "JuliaImportPackage" );

#! @Description
#! This global variable represents the &Julia; module <C>Main</C>,
#! see <Ref Filt="IsJuliaModule" Label="for IsJuliaWrapper and IsRecord"/>.
#! see <Ref Filt="IsJuliaModule" Label="for IsJuliaObject and IsRecord"/>.
#!
#! The variables from the underlying &Julia; session can be accessed via
#! <Ref Var="Julia"/>, as follows.
Expand Down
8 changes: 6 additions & 2 deletions src/adapter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,6 @@ Random.Sampler(::Type{<:AbstractGAPRNG}, x::AbstractVector, ::Random.Repetition)
Base.literal_pow(::typeof(^), x::GapObj, ::Val{-1}) = Wrappers.InverseSameMutability(x)

# iteration

function Base.iterate(obj::GapObj)
if Wrappers.IsList(obj)
len = Wrappers.Length(obj)
Expand All @@ -496,7 +495,12 @@ end

function Base.iterate(obj::GapObj, (i, len)::Tuple{Int,Int})
i > len && return nothing
ElmList(obj, i), (i+1, len)
res = ElmList(obj, i)
while res === nothing # dangerous if `len` is *larger* than the length
i = i+1
res = ElmList(obj, i)
end
return res, (i+1, len)
end

function Base.iterate(obj::GapObj, iter::GapObj)
Expand Down
6 changes: 6 additions & 0 deletions test/adapter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
vs = Vector{Int}.(gap_iter)
@test vs == [[], [1], [1, 1], [1, 1, 1]]
end

# skip holes when iterating over GAP lists
@test xs == [x for x in s]
@test xs == map(x -> x, s)
l = GAP.evalstr("[1, 2,,,, 6]")
@test collect(l) == [1, 2, 6]
end

@testset "deepcopy" begin
Expand Down

0 comments on commit 5cbad84

Please sign in to comment.