User-defined Functions
ccalc supports user-defined named functions, multiple return values, and anonymous functions (lambdas) using Octave/MATLAB syntax.
Named functions
function result = name(p1, p2)
...
result = expr;
end
Define a function at the top level in the REPL or in a .calc / .m script
file. The function is stored in the workspace and can be called like any built-in.
In script files, functions may appear anywhere — before or after the code
that calls them (see Function hoisting in scripts).
Single return value
function y = square(x)
y = x ^ 2;
end
square(5) % 25
Multiple return values
function [mn, mx, avg] = stats(v)
mn = min(v);
mx = max(v);
avg = mean(v);
end
[lo, hi, mu] = stats([4 7 2 9 1 5 8 3 6]);
% lo = 1 hi = 9 mu = 5
Discarding outputs
Use ~ in the assignment target to ignore individual outputs:
[~, top, ~] = stats([10 30 20]); % top = 30
nargin — optional parameters
nargin holds the number of arguments actually passed:
function y = power_fn(base, exp)
if nargin < 2
exp = 2; % default exponent
end
y = base ^ exp;
end
power_fn(5) % 25 (exp = 2 by default)
power_fn(2, 8) % 256
return — early exit
function result = factorial_r(n)
if n <= 1
result = 1;
return % exit immediately — no further code runs
end
result = n * factorial_r(n - 1);
end
factorial_r(7) % 5040
Scope
Each call gets its own isolated scope:
- The caller’s data variables are not visible inside the function.
- Parameters are bound locally.
- Other functions and lambdas from the caller’s workspace are forwarded, enabling self-recursion and mutual recursion.
function g = gcd_fn(a, b)
while b ~= 0
r = mod(a, b);
a = b;
b = r;
end
g = a;
end
gcd_fn(252, 105) % 21
Anonymous functions
@(params) expr creates an anonymous function (lambda):
sq = @(x) x ^ 2;
hyp = @(a, b) sqrt(a^2 + b^2);
sq(7) % 49
hyp(3, 4) % 5
Lexical capture
A lambda captures the value of free variables at definition time:
rate = 0.05;
interest = @(p, n) p * (1 + rate) ^ n;
interest(1000, 10) % 1628.89 (uses captured rate = 0.05)
rate = 0.99; % does not affect the already-created lambda
interest(1000, 10) % still 1628.89
Passing functions as arguments
Use @name to pass an existing function, or @(x) expr inline:
function s = midpoint(f, a, b, n)
h = (b - a) / n;
s = 0;
for k = 1:n
s += f(a + (k - 0.5) * h);
end
s *= h;
end
midpoint(@(x) x^2, 0, 1, 1000) % ≈ 0.333333 (∫₀¹ x² dx)
midpoint(@(x) sin(x), 0, pi, 1000) % ≈ 2.000001 (∫₀ᵖⁱ sin x dx)
Functions returning functions
function f = make_adder(c)
f = @(x) x + c;
end
add5 = make_adder(5);
add10 = make_adder(10);
add5(3) % 8
add10(7) % 17
add5(add10(1)) % 16
Documentation comments
Place %-prefixed lines immediately after the function header to
document a function (MATLAB H1-line convention). The REPL command help <name>
displays them:
function t = tri(n)
% Return the nth triangular number T(n) = n*(n+1)/2.
% Usage: t = tri(n)
%
% Example:
% tri(4) → 10
t = n * (n + 1) / 2;
end
>> help tri
Return the nth triangular number T(n) = n*(n+1)/2.
Usage: t = tri(n)
Example:
tri(4) → 10
- Any number of consecutive
%lines form the doc block. - A blank line between the
functionheader and the first%breaks the association — only lines that immediately follow the header are collected. - One leading space after
%is stripped; remaining indentation is preserved, so% exampledisplays asexample. #-style comments work the same way.help <name>works for autoloaded functions on the path before the first call — ccalc loads the file on demand to extract the doc.
Function hoisting in scripts
In a script file (one that does not begin with a function definition), helper
functions may be placed anywhere in the file — including after the code that calls them.
ccalc pre-registers all top-level function definitions before executing the script body,
matching MATLAB/Octave script semantics:
% main code at the top — calls a helper defined further down
result = double_it(7);
fprintf("double_it(7) = %d\n", result); % prints: double_it(7) = 14
% helper function at the bottom
function y = double_it(x)
y = x * 2;
end
This is the standard layout for MATLAB/Octave scripts: keep the main logic at the top and put helper functions at the bottom, where they are out of the way.
REPL difference: In the interactive REPL, functions take effect immediately when entered — a function must be defined before its first call.
Function files and autoload
A .calc (or .m) file that begins with a function definition is a
function file. ccalc handles it differently from a script:
- Only the primary function (the first one) is exposed to the caller’s workspace.
- Any additional functions in the file are local helpers — invisible outside the file, but available to the primary function (MATLAB-style scoping).
- When a function name is called that is not in the workspace, ccalc
automatically searches for
<name>.calc/<name>.mon the current directory and the session path, loads it, and calls it — no explicitsource()required.
% bisect.calc — primary function + private helper
function [c, k] = bisect(fun, a, b, tol)
% help text goes here, right after the function line
steps = ceil(log2((b - a) / tol));
[c, k] = bisect_r(fun, a, b, 0, steps); % calls local helper
end
function [c, k] = bisect_r(fun, a, b, k, maxSteps)
% bisect_r is local — not visible outside bisect.calc
...
end
If bisect.calc is on the path, calling bisect(...) without any source()
works automatically:
[c, k] = bisect(@(x) x^2 - 2, 1, 2, 1e-8) % bisect.calc auto-loaded
source('bisect.calc') still works for explicit loading.
Testing with assert
assert checks a condition and throws an error if it is false.
Use it in scripts and function files to catch programming mistakes early.
% assert(cond) — error if cond is 0, NaN, or empty
assert(1 == 1) % passes — silently returns
assert(2 > 3) % fails: "assert: condition is false"
% assert(expected, actual) — error if values differ (element-wise)
assert(sqrt(4), 2) % passes
assert([1 2], [1 3]) % fails: "assert: values differ"
% assert(expected, actual, tol) — error if |expected - actual| > tol
assert(pi, 3.14159, 1e-4) % passes — within tolerance
assert(pi, 3.14, 1e-4) % fails — difference 0.00159 > 1e-4
assert works on scalars, vectors, and matrices. For numeric comparisons
the element-wise absolute difference is checked; for matrices the check
applies to every element.
Full example
ccalc examples/user_functions.calc
See also: help userfuncs for the
in-REPL reference, and Control Flow for if, for,
while, break, and return.