Skip to content

Commit f1acb51

Browse files
committed
apply the cache only to leaf nodes
1 parent bc51f34 commit f1acb51

File tree

2 files changed

+38
-15
lines changed

2 files changed

+38
-15
lines changed

src/functor.jl

+28-9
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,24 @@ function _default_walk(f, x)
4040
end
4141

4242
usecache(x) = !isbits(x)
43+
usecache(x::Union{String, Symbol}) = false
4344

4445
struct NoKeyword end
4546

4647
function fmap(f, x; exclude = isleaf, walk = _default_walk, cache = usecache(x) ? IdDict() : nothing, prune = NoKeyword())
47-
usecache(x) && haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
48-
xnew = exclude(x) ? f(x) : walk(x -> fmap(f, x; exclude=exclude, walk=walk, cache=cache, prune=prune), x)
49-
usecache(x) && setindex!(cache, xnew, x)
50-
return xnew
48+
if exclude(x)
49+
if usecache(x)
50+
if haskey(cache, x)
51+
prune isa NoKeyword ? cache[x] : prune
52+
else
53+
cache[x] = f(x)
54+
end
55+
else
56+
f(x)
57+
end
58+
else
59+
walk(x -> fmap(f, x; exclude = exclude, walk = walk, cache = cache, prune = prune), x)
60+
end
5161
end
5262

5363
###
@@ -73,11 +83,20 @@ end
7383
### Vararg forms
7484
###
7585

76-
function fmap(f, x, ys...; exclude = isleaf, walk = _default_walk, cache = IdDict(), prune = NoKeyword())
77-
usecache(x) && haskey(cache, x) && return prune isa NoKeyword ? cache[x] : prune
78-
xnew = exclude(x) ? f(x, ys...) : walk((xy...,) -> fmap(f, xy...; exclude=exclude, walk=walk, cache=cache, prune=prune), x, ys...)
79-
usecache(x) && setindex!(cache, xnew, x)
80-
return xnew
86+
function fmap(f, x, ys...; exclude = isleaf, walk = _default_walk, cache = usecache(x) ? IdDict() : nothing, prune = NoKeyword())
87+
if exclude(x)
88+
if usecache(x)
89+
if haskey(cache, x)
90+
prune isa NoKeyword ? cache[x] : prune
91+
else
92+
cache[x] = f(x, ys...)
93+
end
94+
else
95+
f(x, ys...)
96+
end
97+
else
98+
walk((xy...,) -> fmap(f, xy...; exclude = exclude, walk = walk, cache = cache, prune = prune), x, ys...)
99+
end
81100
end
82101

83102
function _default_walk(f, x, ys...)

test/basics.jl

+10-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct NoChildren2; x; y; end
1414

1515
struct NoChild{T}; x::T; end
1616

17+
1718
###
1819
### Basic functionality
1920
###
@@ -56,13 +57,13 @@ end
5657
m1p = fmapstructure(identity, m1; prune = nothing)
5758
@test m1p == (x = [1, 2, 3], y = (x = [1, 2, 3], y = (x = nothing, y = [1, 2, 3])))
5859

59-
# A non-leaf node can also be repeated:
60+
# The cache applies only to leaf nodes, so that "4" is not shared:
6061
m2 = Foo(Foo(shared, 4), Foo(shared, 4))
6162
@test m2.x === m2.y
6263
m2f = fmap(float, m2)
6364
@test m2f.x.x === m2f.y.x
6465
m2p = fmapstructure(identity, m2; prune = Bar(0))
65-
@test m2p == (x = (x = [1, 2, 3], y = 4), y = Bar(0))
66+
@test m2p == (x = (x = [1, 2, 3], y = 4), y = (x = Bar{Int64}(0), y = 4))
6667

6768
# Repeated isbits types should not automatically be regarded as shared:
6869
m3 = Foo(Foo(shared, 1:3), Foo(1:3, shared))
@@ -75,15 +76,18 @@ end
7576
@test_skip 0 == @allocated fmap(float, (x=1, y=(2, 3), z=4:5))
7677

7778
@testset "usecache" begin
79+
# Leaf types:
7880
@test usecache([1,2])
79-
@test usecache(Ref(3))
80-
8181
@test !usecache(4.0)
82+
@test usecache(NoChild([1,2]))
83+
@test !usecache(NoChild((3,4)))
84+
85+
# Not leaf by default, but `exclude` can change that:
86+
@test usecache(Ref(3))
8287
@test !usecache((5, 6.0))
8388
@test !usecache((a = 2pi, b = missing))
8489

85-
@test usecache(Bar([1,2]))
86-
@test !usecache(Bar((3,4)))
90+
@test usecache((x = [1,2,3], y = 4))
8791
end
8892
end
8993

0 commit comments

Comments
 (0)