LeetCode 2666 - Allow One Function Call
This problem asks us to create a wrapper around an existing function fn such that the wrapped version can only execute the original function one time. In other words, we are given a function fn, and we must return a new function.
Difficulty: 🟢 Easy
Topics: —
Solution
Problem Understanding
This problem asks us to create a wrapper around an existing function fn such that the wrapped version can only execute the original function one time.
In other words, we are given a function fn, and we must return a new function. This returned function behaves exactly like fn during its first invocation, meaning it accepts the same arguments and returns the same result. However, after that first successful execution, every future call must immediately return undefined without calling fn again.
The key requirement is that the original function must only be executed at most once. This means we need some way to remember whether the function has already been called.
The input consists of a function fn, which can take arbitrary arguments. The examples show functions receiving multiple parameters, but our solution must work for any number of arguments because JavaScript functions are flexible. The returned function should preserve the same argument behavior by forwarding all received arguments to fn.
The expected output is not a direct value, but rather a modified function. When this returned function is called for the first time, it executes fn and returns the result. Every later call returns undefined.
The constraints are very small. The number of calls is between 1 and 10, and argument lists are relatively short. Because of this, efficiency is not a major concern. However, the problem is testing understanding of function closures and state persistence, not algorithmic performance.
There are several important edge cases to consider. The wrapped function may be called only once, meaning we should still return the proper result without issue. The wrapped function may also receive different arguments on later calls, but those arguments should be ignored because fn must never execute again. Another important detail is that fn could return any valid JavaScript value, including 0, false, or null, so we cannot determine whether the function was already called by checking the return value. Instead, we must explicitly track execution state.
Approaches
Brute Force Approach
A brute-force way to solve this problem is to store all previous invocations in a collection and check whether the function has already been executed before running it again.
For example, we could maintain an array of previous calls. Each time the returned function executes, we check whether this array is empty. If it is empty, we call fn, save information about the invocation, and return the result. Otherwise, we return undefined.
This approach is correct because it ensures that fn only executes once. However, it uses unnecessary storage and bookkeeping. Since we only care whether the function has already been called, storing invocation history is excessive.
Optimal Approach
The key observation is that we only need one piece of information: whether fn has already been executed.
A simple boolean variable is sufficient. We can initialize a flag such as called = false. The returned function checks this flag whenever it runs:
- If
calledisfalse, mark it astrue, executefn, and return the result. - If
calledis alreadytrue, returnundefined.
This works because closures allow the returned function to remember variables from the surrounding scope, even after the outer function has finished executing. The boolean state persists between calls.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(1) | O(1) | Stores unnecessary call history or extra bookkeeping |
| Optimal | O(1) | O(1) | Uses a closure with a boolean flag |
Although both approaches have constant complexity in practice, the optimal solution is cleaner and uses only the minimum necessary state.
Algorithm Walkthrough
- Define a boolean variable, such as
called, and initialize it tofalse. This variable tracks whetherfnhas already executed. - Return a new function that accepts arbitrary arguments. This ensures the wrapper behaves like the original function regardless of parameter count.
- Inside the returned function, check the
calledflag. If it is alreadytrue, immediately returnundefined. This prevents future executions offn. - If
calledisfalse, set it totruebefore callingfn. Updating the flag first guarantees that even iffnbehaves unexpectedly, future calls remain blocked. - Execute
fnusing the provided arguments and return its result.
Why it works
The correctness comes from maintaining a simple invariant: once called becomes true, it never changes back to false. Because closures preserve the variable across function calls, every invocation of the returned function sees the updated state. Therefore, fn executes exactly once, and all later calls return undefined.
Python Solution
Since this is a JavaScript function problem on LeetCode, the Python equivalent below demonstrates the same closure-based logic in Python syntax.
from typing import Callable, Any
class Solution:
def once(self, fn: Callable[..., Any]) -> Callable[..., Any]:
called = False
def wrapper(*args, **kwargs):
nonlocal called
if called:
return None # Python equivalent of undefined
called = True
return fn(*args, **kwargs)
return wrapper
The implementation starts by defining a boolean variable named called and setting it to False. This variable exists in the outer scope of the once method and acts as persistent state.
The nested wrapper function accepts arbitrary positional and keyword arguments using *args and **kwargs. This mirrors the flexibility of JavaScript functions and allows the wrapper to support any function signature.
Inside wrapper, the nonlocal keyword allows modification of the called variable defined in the enclosing scope. If called is already True, the function immediately returns None, which acts as the Python equivalent of JavaScript's undefined.
If the function has not been executed yet, the flag is updated to True, and fn is invoked with the provided arguments. The result of fn is returned directly.
Go Solution
Go does not support JavaScript-style variadic dynamic typing in the same way, but closures work similarly. We can use a boolean variable captured by the returned function.
package main
func once(fn func(...interface{}) interface{}) func(...interface{}) interface{} {
called := false
return func(args ...interface{}) interface{} {
if called {
return nil
}
called = true
return fn(args...)
}
}
The Go implementation uses a closure over the called variable. Because functions in Go can capture variables from their surrounding scope, the boolean state persists across invocations.
Instead of returning undefined, Go returns nil, which is the closest equivalent. The function accepts ...interface{} so that arbitrary arguments can be passed, similar to JavaScript's flexible parameter system.
Worked Examples
Example 1
Input:
fn = (a,b,c) => (a + b + c)
calls = [[1,2,3],[2,3,6]]
Initially:
| Variable | Value |
|---|---|
| called | false |
First Call: onceFn(1, 2, 3)
| Step | Action | called | Result |
|---|---|---|---|
| 1 | Check flag | false | Continue |
| 2 | Set called = true |
true | Continue |
| 3 | Execute fn(1,2,3) |
true | 6 |
Returned value: 6
Second Call: onceFn(2, 3, 6)
| Step | Action | called | Result |
|---|---|---|---|
| 1 | Check flag | true | Return undefined |
Final output:
[{"calls":1,"value":6}]
Example 2
Input:
fn = (a,b,c) => (a * b * c)
calls = [[5,7,4],[2,3,6],[4,6,8]]
Initially:
| Variable | Value |
|---|---|
| called | false |
First Call: onceFn(5, 7, 4)
| Step | Action | called | Result |
|---|---|---|---|
| 1 | Check flag | false | Continue |
| 2 | Set called = true |
true | Continue |
| 3 | Execute fn(5,7,4) |
true | 140 |
Returned value: 140
Second Call: onceFn(2, 3, 6)
| Step | Action | called | Result |
|---|---|---|---|
| 1 | Check flag | true | Return undefined |
Third Call: onceFn(4, 6, 8)
| Step | Action | called | Result |
|---|---|---|---|
| 1 | Check flag | true | Return undefined |
Final output:
[{"calls":1,"value":140}]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | Each invocation only checks a boolean and possibly executes fn |
| Space | O(1) | Only one boolean variable is stored |
The time complexity is constant because every function call performs a simple boolean check and possibly one function invocation. No loops or additional data structures are used. The space complexity is also constant because the implementation only stores a single boolean flag.
Test Cases
from typing import Callable, Any
class Solution:
def once(self, fn: Callable[..., Any]) -> Callable[..., Any]:
called = False
def wrapper(*args, **kwargs):
nonlocal called
if called:
return None
called = True
return fn(*args, **kwargs)
return wrapper
solution = Solution()
# Example 1: addition function
once_fn = solution.once(lambda a, b, c: a + b + c)
assert once_fn(1, 2, 3) == 6 # first call executes function
assert once_fn(2, 3, 6) is None # second call blocked
# Example 2: multiplication function
once_fn = solution.once(lambda a, b, c: a * b * c)
assert once_fn(5, 7, 4) == 140 # first call executes
assert once_fn(2, 3, 6) is None # second call blocked
assert once_fn(4, 6, 8) is None # third call blocked
# Single call only
once_fn = solution.once(lambda x: x * 2)
assert once_fn(5) == 10 # first and only call
# Function returning zero
once_fn = solution.once(lambda: 0)
assert once_fn() == 0 # valid falsy return value
assert once_fn() is None # second call blocked
# Function returning False
once_fn = solution.once(lambda: False)
assert once_fn() is False # valid boolean result
assert once_fn() is None # second call blocked
# No argument function
once_fn = solution.once(lambda: 42)
assert once_fn() == 42 # works without parameters
assert once_fn() is None # blocked afterward
# Different arguments on later calls
once_fn = solution.once(lambda a, b: a + b)
assert once_fn(10, 20) == 30 # first call succeeds
assert once_fn(999, 999) is None # arguments ignored
| Test | Why |
|---|---|
| Addition example | Validates the first provided example |
| Multiplication example | Validates repeated blocking behavior |
| Single call | Ensures correct behavior when only invoked once |
Function returning 0 |
Verifies falsy values are handled correctly |
Function returning False |
Ensures boolean return values do not break logic |
| No argument function | Confirms flexible function signature support |
| Different later arguments | Verifies later arguments are ignored |
Edge Cases
One important edge case is when the original function returns a falsy value such as 0, False, or None. A naive implementation might mistakenly use the returned value to determine whether the function has already been called. For example, checking whether a stored result exists would fail for 0 or False. This implementation avoids that issue entirely by tracking execution using a dedicated boolean flag.
Another important case occurs when the wrapped function is only called once. Some implementations accidentally assume multiple invocations and may mishandle initialization or state transitions. In this implementation, the first call simply executes normally and updates the flag, so a single invocation works without special handling.
A third edge case is when later calls use completely different arguments. Since the problem guarantees the function may only execute once, later arguments must be ignored entirely. The implementation checks the boolean flag before attempting any execution, ensuring that subsequent inputs have no effect.
Finally, functions with varying numbers of arguments must be handled correctly. Some functions may take zero parameters while others may take many. By using *args and **kwargs in Python, and variadic arguments in Go, the implementation supports any function signature without modification.