For those who wonder how this works, you have a magical variable nargout in your function, which you can check to see how many outputs the caller will receive, like this:
Similarly you have a nargin variable available for the input arguments, which can be used to assign default values to left out arguments, or other transformations of the arguments, e.g.
I’m particularly happy about having left that language feature behind me.
An analogous behavior (conditional computation of outputs) can be achieved in various ways:
Simply give the function different names for different numbers of outputs.
Return an object that computes the additional outputs as requested, for example by returning an iterator. e.g. Julia’s lu(A) function for LU factorization returns a factorization object F, which can either be used as-is to solve linear systems via x = F \ b or can be used to compute factors F.L, F.U as desired (which are otherwise not computed explicitly — this is accomplished by overloading getproperty in Julia), and which is also iterable so you can do L, U = lu(a) or L, U = F, for example.
In-place functions, conventionally written in Julia as f!(x,y). If you don’t want a particular output, either don’t pass it — call f!(x) and dispatch to a different method — or pass some sentinel value (e.g. nothing).
Option 2 is the closest analogue of the Matlab nargout, and syntactically looks very similar to the user if an iterable object is returned.
I don’t really think this is that great of syntax, but I had a bit of fun making a macro to do this with a slight change and a bit of Julia flavour.
using MacroTools: @capture
using ExprTools: splitdef, combinedef
macro m(ex)
@capture(ex, [args__] = fdef_) || error("Malformed input syntax")
d = splitdef(fdef)
d[:body] = :($(d[:body]); (; $(args...),))
esc(combinedef(d))
end
Here it is in action:
julia> @m [m, s] = function stat(x)
n = length(x);
m = sum(x)/n;
s = sqrt(sum((x .- m).^2/n));
end
stat (generic function with 1 method)
julia> stat(rand(100))
(m = 0.46880423584214137, s = 0.2769434039119943)
One thing that’s kinda nice is that this returns the outputs as a NamedTuple, but if you prefer a regular Tuple, you could instead do
macro m(ex)
@capture(ex, [args__] = fdef_) || error("Malformed input syntax")
d = splitdef(fdef)
d[:body] = :($(d[:body]); ($(args...),))
esc(combinedef(d))
end
I guess the important thing to highlight is that Julia is such a wonderfully hackable language with very powerful metaprogramming, that it’s actually quite easy to mould the language syntax to your needs.
As of MATLAB 2020, there’s a keyword arguments which lets you define defaults, assertations etc. It’s pretty good (for MATLAB) but it’s nowhere near as nice as just doing this in the signature.
(And MATLAB 2021a introduced a keyword=argument syntax as a parallel to the existing (and awful) 'keyword', argument)
&& is a shortcuting operator, which means the second term isn’t evaluated if the first one is false, whereas & evaluates everything:
julia> true && (println("a"); true)
a
true
julia> false && (println("a"); true)
false
julia> false & (println("a"); true)
a
false
the short-cutting behaviour doesn’t work for .&& though, it behaves like .& but with the nicer precedence: for the short-circuit with .&&, there must be a broadcasted function, see this answer and the 2
Haha I remember writing a MatLab function to automatically generate the text of the most common nargin and nargout code based on a function signature (also in text), which I would then just paste into the top of my new function. Oh unhappy days.
Hi, @leon! I just wanted to say hello and thank you for the suggestions and say that I hope the barrage of replies doesn’t feel overwhelming or unfriendly. People are just excited about programming languages here (especially Julia, of course), and being helpful. It seems like there are ways to do all these things, even if they’re not exactly like they are done in Matlab. Please don’t hesitate to ask about any other things that feel more awkward than they should—there may already be a way to express it better… or it may be an opportunity for us to improve the language and make something awkward a little easier, which is always exciting!
Many thanks for the very kind reply, Stefan! No worries, I find most of the replies to be very helpful. It’s also ok to push back on some of my suggestions. I now come to appreciate the Julia way of defining a function, for example.
As a newbie, I start to love Julia a lot. Therefore, I just hope Julia can be improved in certain ways.
I got lots of help from lots of people in this community recently and I’m wholeheartedly grateful. Thank you so much, Everyone!
Just to play devil’s advocate, you can do essentially the same in Julia:
function f(x, y)
A = stuff
lines
of
code
B = other stuff
more
code
return (A, B)
end
But perhaps requiring the “return” statement (optional return keyword) encourages A and B to be computed close to or immediately before the return statement.