-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
function^n for iterated functions #39042
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,6 +57,7 @@ export | |
IOStream, | ||
LinRange, | ||
Irrational, | ||
IteratedFunction, | ||
Matrix, | ||
MergeSort, | ||
Missing, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -559,15 +559,15 @@ extrema(f, x::Real) = (y = f(x); (y, y)) | |
""" | ||
identity(x) | ||
|
||
The identity function. Returns its argument. | ||
The identity function. Returns its argument. Any keyword arguments are ignored. | ||
|
||
# Examples | ||
```jldoctest | ||
julia> identity("Well, what did you expect?") | ||
"Well, what did you expect?" | ||
``` | ||
""" | ||
identity(x) = x | ||
identity(x; kws...) = x | ||
|
||
+(x::Number) = x | ||
*(x::Number) = x | ||
|
@@ -931,7 +931,7 @@ julia> fs = [ | |
julia> ∘(fs...)(3) | ||
3.0 | ||
``` | ||
See also [`ComposedFunction`](@ref). | ||
See also [`ComposedFunction`](@ref) and [`IteratedFunction`](@ref). | ||
""" | ||
function ∘ end | ||
|
||
|
@@ -940,9 +940,9 @@ function ∘ end | |
|
||
Represents the composition of two callable objects `outer::Outer` and `inner::Inner`. That is | ||
```julia | ||
ComposedFunction(outer, inner)(args...; kw...) === outer(inner(args...; kw...)) | ||
ComposedFunction(outer, inner)(args...; kw...) === outer(inner(args...; kw...); kw...) | ||
``` | ||
The preferred way to construct instance of `ComposedFunction` is to use the composition operator [`∘`](@ref): | ||
The preferred way to construct an instance of `ComposedFunction` is to use the composition operator [`∘`](@ref): | ||
```jldoctest | ||
julia> sin ∘ cos === ComposedFunction(sin, cos) | ||
true | ||
|
@@ -962,7 +962,7 @@ julia> composition.inner === cos | |
true | ||
``` | ||
!!! compat "Julia 1.6" | ||
ComposedFunction requires at least Julia 1.6. In earlier versions `∘` returns an anonymous function instead. | ||
`ComposedFunction` requires at least Julia 1.6. In earlier versions `∘` returns an anonymous function instead. | ||
|
||
See also [`∘`](@ref). | ||
""" | ||
|
@@ -973,7 +973,7 @@ struct ComposedFunction{O,I} <: Function | |
ComposedFunction(outer, inner) = new{Core.Typeof(outer),Core.Typeof(inner)}(outer, inner) | ||
end | ||
|
||
(c::ComposedFunction)(x...) = c.outer(c.inner(x...)) | ||
(c::ComposedFunction)(x...; kws...) = c.outer(c.inner(x...; kws...); kws...) | ||
|
||
∘(f) = f | ||
∘(f, g) = ComposedFunction(f, g) | ||
|
@@ -985,6 +985,54 @@ function show(io::IO, c::ComposedFunction) | |
show(io, c.inner) | ||
end | ||
|
||
""" | ||
IteratedFunction{F} <: Function | ||
|
||
`IteratedFunction(f,n)` represents the function `f` iterated `n ≥ 0` times on | ||
its input, which must be a single argument. | ||
|
||
That is, for functions `f(x)`, it represents the function | ||
`x -> f(f(f(f(...(f(x))))))` iterated `n` times`. Any keyword arguments | ||
are passed through to all calls. | ||
|
||
If `f isa Function`, you should normally use the construction `f^n` | ||
to form an `IteratedFunction`. If `n` is a literal integer, `f^n` | ||
may construct a more specialized object, e.g. `f^1 == f`, `f^0 == identity`, | ||
and `f^2 == f ∘ f`. | ||
|
||
!!! compat "Julia 1.7" | ||
`IteratedFunction` and `f^n` require at least Julia 1.7. | ||
|
||
See also [`∘`](@ref) and [`ComposedFunction`](@ref). | ||
""" | ||
struct IteratedFunction{F} <: Function | ||
f::F | ||
n::Int | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this should be a type parameter instead, so application can be type stable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about that — it depends on how this is used. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe one could get the best of both words? Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it really that common to construct a lot of these with dynamic |
||
IteratedFunction{F}(f, n::Integer) where {F} = new{F}(f, _check_nonnegative(n)) | ||
IteratedFunction(f, n::Integer) = new{Core.Typeof(f)}(f, _check_nonnegative(n)) | ||
end | ||
_check_nonnegative(n::Integer) = n ≥ 0 ? n : throw(ArgumentError("$n is not ≥ 0")) | ||
^(f::Function, n::Integer) = IteratedFunction(f, n) | ||
^(fn::IteratedFunction, n::Integer) = IteratedFunction(fn.f, fn.n * n) | ||
literal_pow(::typeof(^), f::Function, ::Val{0}) = identity | ||
literal_pow(::typeof(^), f::Function, ::Val{1}) = f | ||
literal_pow(::typeof(^), f::Function, ::Val{2}) = f ∘ f | ||
literal_pow(::typeof(^), f::Function, ::Val{3}) = f ∘ f ∘ f | ||
literal_pow(::typeof(^), f::Function, ::Val{4}) = f ∘ f ∘ f ∘ f | ||
literal_pow(::typeof(^), fn::IteratedFunction, ::Val{2}) = IteratedFunction(fn.f, fn.n * 2) | ||
literal_pow(::typeof(^), fn::IteratedFunction, ::Val{3}) = IteratedFunction(fn.f, fn.n * 3) | ||
literal_pow(::typeof(^), fn::IteratedFunction, ::Val{4}) = IteratedFunction(fn.f, fn.n * 4) | ||
function (fn::IteratedFunction)(x; kws...) | ||
for i in Base.OneTo(fn.n) | ||
x = fn.f(x; kws...) | ||
end | ||
return x | ||
end | ||
function show(io::IO, fn::IteratedFunction) | ||
show(io, fn.f) | ||
print(io, "^", fn.n) | ||
end | ||
|
||
""" | ||
!f::Function | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is the right definition. Arguments to the composed function should only be passed to
inner
. If it were possible forinner
to return something that caused keyword arguments to be passed toouter
, then that would be the right thing to do. Since that isn't possible, the outer function should never be passed keyword arguments.The situation is similar to that with varargs: the inner function can take any number of arguments, but the outer function can only accept the inner function's returned value.