-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathImproperMemoization.qhelp
More file actions
84 lines (74 loc) · 2.29 KB
/
ImproperMemoization.qhelp
File metadata and controls
84 lines (74 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
A common pattern in Ruby is to memoize the result of a method by storing
it in an instance variable. If the method has no parameters, it can simply
be stored directly in a variable.
</p>
<sample language="ruby">
def answer
@answer ||= calculate_answer
end
</sample>
<p>
If the method takes parameters, then there are multiple results to store
(one for each combination of parameter value). In this case the values
should be stored in a hash, keyed by the parameter values.
</p>
<sample language="ruby">
def answer(x, y)
@answer ||= {}
@answer[x] ||= {}
@answer[x][y] ||= calculate_answer
end
</sample>
<p>
If a memoization method takes parameters but does not include them in the
memoization key, subsequent calls to the method with different parameter
values may incorrectly return the same result. This can lead to the method
returning stale data, or leaking sensitive information.
</p>
</overview>
<example>
<p>
In this example, the method does not include its parameters in the
memoization key. The first call to this method will cache the result, and
subsequent calls will return the same result regardless of what arguments
are given.
</p>
<sample language="ruby">
def answer(x, y)
@answer ||= calculate_answer(x, y)
end
</sample>
<p>
This can be fixed by storing the result of <code>calculate_answer</code>
in a hash, keyed by the parameter values.
</p>
<sample language="ruby">
def answer(x, y)
@answer ||= {}
@answer[x] ||= {}
@answer[x][y] ||= calculate_answer(x, y)
end
</sample>
<p>
Note that if the result of <code>calculate_answer</code> is
<code>false</code> or <code>nil</code>, then it will not be cached.
To cache these values you can use a different pattern:
</p>
<sample language="ruby">
def answer(x, y)
@answer ||= Hash.new do |h1, x|
h1[x] = Hash.new do |h2, y|
h2[y] = calculate_answer(x, y)
end
end
@answer[x][y]
end
</sample>
</example>
</qhelp>