Scala函数外部变量访问问题:结合备忘化示例代码解析
Great question! Let's break this down clearly—first explaining how Scala handles access to variables outside a function, then walking through that memoization code you highlighted.
Scala's Mechanism for Accessing Variables Outside a Function
In Scala, when an inner function (like the anonymous i => { ... } in your code) accesses variables or functions defined in an outer scope, this is enabled by closures. Here's the key details:
- A closure captures a reference to the outer variable, not just its current value. This means if the outer variable is mutable (declared with
var), the inner function can see and modify updates to that variable over time. - For immutable variables (declared with
val), the closure captures the fixed value once, since it can't be changed later. - This allows inner functions to maintain state across multiple calls—exactly what's used in memoization to cache results.
Breaking Down the Memoization Code
First, let's fill in the implied else part of your code to make the logic complete:
def memoizedIsPrime: Int => Boolean = { def checkIfPrime(i: Int): Boolean = { 2 to (i - 1) forall (x => i % x != 0) } var cache = Map.empty[Int, Boolean] i => { if (!cache.contains(i)) { print(s"Calling isPrime since input ${i} has not been seen before and caching the output") cache = cache updated(i, checkIfPrime(i)) } cache(i) } }
Now let's walk through how this works step by step:
- The
checkIfPrimeinner function: This holds the core logic to determine if a number is prime. It verifies that all integers from 2 up toi-1do not divideievenly. (Note: A more optimized version would check up tosqrt(i), but that's unrelated to the memoization pattern itself.) - The mutable cache:
var cache = Map.empty[Int, Boolean]initializes an empty map to store already computed prime check results. Usingvarlets us update this cache across multiple calls tomemoizedIsPrime(we're technically reassigning thevarto new immutable maps each time, but it behaves like a mutable cache for our purposes). - The returned closure: The
i => { ... }function is the actual memoized prime checker that gets assigned tomemoizedIsPrime. Here's what happens when you call it with a number:- First, it checks if the input
iis already present in thecache. - If not, it prints a status message, computes the prime check result via
checkIfPrime(i), updates thecachewith the new entry, then returns the computed result. - If
iis already in the cache, it skips the expensive computation and directly returns the stored Boolean value fromcache(i).
- First, it checks if the input
Key Notes About This Implementation
- Side effects: This code has visible side effects (printing to the console, modifying the
cachevariable). In pure functional Scala, you might prefer using immutable state with a wrapper likescala.collection.mutable.Mapor more functional patterns, but this is a simple, intuitive memoization example. - Thread safety: This implementation is not thread-safe. If multiple threads call
memoizedIsPrimeconcurrently, there's a risk of race conditions when updating thecache. For thread-safe memoization, you'd need to use a concurrent collection (likejava.util.concurrent.ConcurrentHashMap) or add synchronization around cache access.
内容的提问来源于stack exchange,提问作者pramesh
相关产品推荐
相关产品推荐

