Rename split-loop-with-lambda to slice-with-lambda
The refactoring is now published as Slice on sammancoaching.org: https://sammancoaching.org/refactorings/slice.html - git mv ideas/split-loop-with-lambda -> ideas/slice-with-lambda - rename learning-hour file and update frontmatter name - update paths in tools/rehearse scripts and report title - update prose: 'Split Loop with Lambda' -> 'Slice with Lambda' in learning hour, exercise READMEs, storyboard, solution comments - link the published Slice refactoring from the pattern draft
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# Rehearsal workspace — refactoring task
|
||||
|
||||
You are refactoring `src/main/java/codingdojo/LongRangeForecasts.java` in this
|
||||
Maven project. Run `mvn -q test` after each change (mvn is on your PATH). It may
|
||||
fail — that is expected: the dependency on `Scorer` makes the method untestable —
|
||||
but the code must still *compile*.
|
||||
|
||||
## Goal
|
||||
|
||||
In `LongRangeForecasts.longRangeForecast()`, the loop over `interestingDates`
|
||||
collects weather by calling `scorer` (an unwanted external dependency).
|
||||
|
||||
1. Introduce a lambda for the scorer-dependent code and move its definition to
|
||||
the top of the method, leaving the invocation where it is. (No refactoring
|
||||
tool supports this — do it as a minimal manual edit.)
|
||||
2. Extract the weather-collection loop into a new method `collectExpectedWeather`
|
||||
that takes the lambda as a parameter (suggested type `LongFunction<Boolean>`).
|
||||
3. Add the lambda as a parameter to `longRangeForecast` itself: the signature
|
||||
must become `longRangeForecast(LongFunction<Boolean> dayOffsetToWeather)`,
|
||||
with a public no-arg overload `longRangeForecast()` that delegates and wraps
|
||||
the `scorer` field in a lambda. Do not keep the `String quarter` parameter.
|
||||
(`java-refactor change-method-signature` or `java-refactor introduce-param`
|
||||
may help; fall back to a minimal manual edit if no subcommand can do it.)
|
||||
4. Stop. Report the final diff.
|
||||
|
||||
## Tool rules (variant C)
|
||||
A semantic Java refactoring CLI, `java-refactor`, is on your PATH
|
||||
(`java-refactor --help` lists the subcommands). Using it is OPTIONAL — you
|
||||
may also change code with your normal file-editing tools. Choose per step
|
||||
whatever you think is most reliable.
|
||||
|
||||
Subcommands that may be relevant for this task:
|
||||
- `extract` — extract selected statements into a new private method
|
||||
(selection given as start/end line+column; `--dry-run` previews)
|
||||
- `introduce-param` / `change-method-signature` — alter a method's parameters
|
||||
- `rename` — rename a symbol project-wide
|
||||
@@ -0,0 +1,62 @@
|
||||
Only in /tmp/rehearsal/runs/C+Q/workspace: AGENTS.md
|
||||
diff -ru '--exclude=target' /tmp/rehearsal/pristine/src/main/java/codingdojo/LongRangeForecasts.java /tmp/rehearsal/runs/C+Q/workspace/src/main/java/codingdojo/LongRangeForecasts.java
|
||||
--- /tmp/rehearsal/pristine/src/main/java/codingdojo/LongRangeForecasts.java 2026-09-27 14:58:14.139167804 +0000
|
||||
+++ /tmp/rehearsal/runs/C+Q/workspace/src/main/java/codingdojo/LongRangeForecasts.java 2026-09-27 15:53:52.292927858 +0000
|
||||
@@ -3,19 +3,18 @@
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
+import java.util.function.LongFunction;
|
||||
|
||||
public class LongRangeForecasts {
|
||||
|
||||
private final Scorer scorer = new Scorer();
|
||||
|
||||
- public Map<IceCream, Integer> longRangeForecast(String quarter) {
|
||||
+ public Map<IceCream, Integer> longRangeForecast() {
|
||||
+ return longRangeForecast(d -> scorer.lookupWeather(d));
|
||||
+ }
|
||||
|
||||
+ public Map<IceCream, Integer> longRangeForecast(LongFunction<Boolean> dayOffsetToWeather) {
|
||||
var result = new HashMap<IceCream, Integer>();
|
||||
- if (Objects.equals(quarter, "Q1") || Objects.equals(quarter, "Q4")) {
|
||||
- for (IceCream flavour : IceCream.values()) {
|
||||
- result.put(flavour, 5);
|
||||
- }
|
||||
- }
|
||||
for (IceCream flavour : IceCream.values()) {
|
||||
result.put(flavour, 0);
|
||||
}
|
||||
@@ -25,15 +24,7 @@
|
||||
Instant.parse("2023-06-05T14:00:00.000-07:00"),
|
||||
Instant.parse("2023-06-23T14:00:00.000-07:00")
|
||||
);
|
||||
- List<Boolean> expectedWeather = new ArrayList<>();
|
||||
- for (Instant interestingDate : interestingDates) {
|
||||
- scorer.updateSelection();
|
||||
- var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
||||
- long daysForward = ChronoUnit.DAYS.between(forecastDate, interestingDate);
|
||||
- Boolean lookupWeather = scorer.lookupWeather(daysForward);
|
||||
- expectedWeather.add(lookupWeather);
|
||||
- }
|
||||
-
|
||||
+ List<Boolean> expectedWeather = collectExpectedWeather(interestingDates, dayOffsetToWeather);
|
||||
for (IceCream flavour : result.keySet()) {
|
||||
result.put(flavour, result.get(flavour) + 10);
|
||||
var sunnyHolidays = expectedWeather.stream().filter(s -> s).count();
|
||||
@@ -46,4 +37,15 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+
|
||||
+ private List<Boolean> collectExpectedWeather(List<Instant> dates, LongFunction<Boolean> dayOffsetToWeather) {
|
||||
+ List<Boolean> expectedWeather = new ArrayList<>();
|
||||
+ for (Instant date : dates) {
|
||||
+ scorer.updateSelection();
|
||||
+ var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
||||
+ long daysForward = ChronoUnit.DAYS.between(forecastDate, date);
|
||||
+ expectedWeather.add(dayOffsetToWeather.apply(daysForward));
|
||||
+ }
|
||||
+ return expectedWeather;
|
||||
+ }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
--- Run C+Q ---
|
||||
session file: /tmp/rehearsal/runs/C+Q/session/2026-09-27T15-53-02-700Z_01a0e391-ddec-76b3-83b6-8a55d163bf7c.jsonl
|
||||
model: Qwen/Qwen3.6-35B-A3B
|
||||
session span: 71.7 s
|
||||
assistant messages: 16
|
||||
tool calls: 17 total
|
||||
bash 7
|
||||
read 5
|
||||
edit 5
|
||||
tokens input: 138747
|
||||
tokens output: 8337
|
||||
tokens cacheRead: 0
|
||||
tokens cacheWrite: 0
|
||||
tokens reasoning: 2579
|
||||
tokens TOTAL (sum): 149663
|
||||
--- /usr/bin/time -v (wall clock + max RSS) ---
|
||||
Elapsed (wall clock) time (h:mm:ss or m:ss): 1:11.84
|
||||
Maximum resident set size (kbytes): 226488
|
||||
Exit status: 0
|
||||
--- pi stdout (last 15 lines) ---
|
||||
+ long daysForward = ChronoUnit.DAYS.between(forecastDate, date);
|
||||
+ expectedWeather.add(dayOffsetToWeather.apply(daysForward));
|
||||
+ }
|
||||
+ return expectedWeather;
|
||||
+ }
|
||||
}
|
||||
```
|
||||
|
||||
## Summary of changes:
|
||||
|
||||
| Step | Change |
|
||||
|------|--------|
|
||||
| **1** | Added `import java.util.function.LongFunction`; introduced `dayOffsetToWeather` lambda wrapping `scorer.lookupWeather()` in the original method body, replacing direct scorer calls |
|
||||
| **2** | Extracted the weather-collection loop into a new `private List<Boolean> collectExpectedWeather(List<Instant>, LongFunction<Boolean>)` method |
|
||||
| **3** | Removed `String quarter` parameter; added a **no-arg** `longRangeForecast()` that delegates to the parameterized overload wrapping the `scorer`; renamed the main method to accept `LongFunction<Boolean> dayOffsetToWeather` |
|
||||
@@ -0,0 +1,71 @@
|
||||
The test compilation error is expected — the test still calls the old `longRangeForecast("Q1")` signature which has been replaced by the no-arg overload and the new `LongFunction<Boolean>` parameterized version, per the task requirements. The AGENTS.md notes that test failures are expected.
|
||||
|
||||
---
|
||||
|
||||
## Refactoring complete. Final diff of `LongRangeForecasts.java`:
|
||||
|
||||
```diff
|
||||
import java.util.*;
|
||||
+import java.util.function.LongFunction;
|
||||
|
||||
public class LongRangeForecasts {
|
||||
|
||||
private final Scorer scorer = new Scorer();
|
||||
|
||||
- public Map<IceCream, Integer> longRangeForecast(String quarter) {
|
||||
+ public Map<IceCream, Integer> longRangeForecast() {
|
||||
+ return longRangeForecast(d -> scorer.lookupWeather(d));
|
||||
+ }
|
||||
|
||||
+ public Map<IceCream, Integer> longRangeForecast(LongFunction<Boolean> dayOffsetToWeather) {
|
||||
var result = new HashMap<IceCream, Integer>();
|
||||
- if (Objects.equals(quarter, "Q1") || Objects.equals(quarter, "Q4")) {
|
||||
- for (IceCream flavour : IceCream.values()) {
|
||||
- result.put(flavour, 5);
|
||||
- }
|
||||
- }
|
||||
for (IceCream flavour : IceCream.values()) {
|
||||
result.put(flavour, 0);
|
||||
}
|
||||
@@ -25,15 +24,7 @@
|
||||
Instant.parse("2023-06-05T14:00:00.000-07:00"),
|
||||
Instant.parse("2023-06-23T14:00:00.000-07:00")
|
||||
);
|
||||
- List<Boolean> expectedWeather = new ArrayList<>();
|
||||
- for (Instant interestingDate : interestingDates) {
|
||||
- scorer.updateSelection();
|
||||
- var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
||||
- long daysForward = ChronoUnit.DAYS.between(forecastDate, interestingDate);
|
||||
- Boolean lookupWeather = scorer.lookupWeather(daysForward);
|
||||
- expectedWeather.add(lookupWeather);
|
||||
- }
|
||||
-
|
||||
+ List<Boolean> expectedWeather = collectExpectedWeather(interestingDates, dayOffsetToWeather);
|
||||
for (IceCream flavour : result.keySet()) {
|
||||
result.put(flavour, result.get(flavour) + 10);
|
||||
var sunnyHolidays = expectedWeather.stream().filter(s -> s).count();
|
||||
@@ -46,4 +37,15 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+
|
||||
+ private List<Boolean> collectExpectedWeather(List<Instant> dates, LongFunction<Boolean> dayOffsetToWeather) {
|
||||
+ List<Boolean> expectedWeather = new ArrayList<>();
|
||||
+ for (Instant date : dates) {
|
||||
+ scorer.updateSelection();
|
||||
+ var forecastDate = Instant.parse("2023-04-26T14:00:00.000-07:00");
|
||||
+ long daysForward = ChronoUnit.DAYS.between(forecastDate, date);
|
||||
+ expectedWeather.add(dayOffsetToWeather.apply(daysForward));
|
||||
+ }
|
||||
+ return expectedWeather;
|
||||
+ }
|
||||
}
|
||||
```
|
||||
|
||||
## Summary of changes:
|
||||
|
||||
| Step | Change |
|
||||
|------|--------|
|
||||
| **1** | Added `import java.util.function.LongFunction`; introduced `dayOffsetToWeather` lambda wrapping `scorer.lookupWeather()` in the original method body, replacing direct scorer calls |
|
||||
| **2** | Extracted the weather-collection loop into a new `private List<Boolean> collectExpectedWeather(List<Instant>, LongFunction<Boolean>)` method |
|
||||
| **3** | Removed `String quarter` parameter; added a **no-arg** `longRangeForecast()` that delegates to the parameterized overload wrapping the `scorer`; renamed the main method to accept `LongFunction<Boolean> dayOffsetToWeather` |
|
||||
@@ -0,0 +1 @@
|
||||
Read AGENTS.md in this directory and execute the refactoring task it describes. Run mvn -q test after each change. When done, stop and report the final diff of LongRangeForecasts.java.
|
||||
@@ -0,0 +1,23 @@
|
||||
Command being timed: "timeout -k 15 600 pi -p -a --provider entrim --model Qwen/Qwen3.6-35B-A3B --session-dir /tmp/rehearsal/runs/C+Q/session --name rehearsal-C+Q Read AGENTS.md in this directory and execute the refactoring task it describes. Run mvn -q test after each change. When done, stop and report the final diff of LongRangeForecasts.java."
|
||||
User time (seconds): 15.34
|
||||
System time (seconds): 1.11
|
||||
Percent of CPU this job got: 22%
|
||||
Elapsed (wall clock) time (h:mm:ss or m:ss): 1:11.84
|
||||
Average shared text size (kbytes): 0
|
||||
Average unshared data size (kbytes): 0
|
||||
Average stack size (kbytes): 0
|
||||
Average total size (kbytes): 0
|
||||
Maximum resident set size (kbytes): 226488
|
||||
Average resident set size (kbytes): 0
|
||||
Major (requiring I/O) page faults: 0
|
||||
Minor (reclaiming a frame) page faults: 322002
|
||||
Voluntary context switches: 32592
|
||||
Involuntary context switches: 810
|
||||
Swaps: 0
|
||||
File system inputs: 0
|
||||
File system outputs: 920
|
||||
Socket messages sent: 0
|
||||
Socket messages received: 0
|
||||
Signals delivered: 0
|
||||
Page size (bytes): 4096
|
||||
Exit status: 0
|
||||
@@ -0,0 +1,10 @@
|
||||
--- Run C+Q ---
|
||||
final diff vs pristine: 30 changed lines -> /tmp/rehearsal/runs/C+Q/diff.patch
|
||||
mvn test with solution's test + approved file:
|
||||
RESULT: PASS (deterministic)
|
||||
structural checks:
|
||||
[x] method collectExpectedWeather exists
|
||||
[x] takes LongFunction<Boolean> parameter
|
||||
[i] note: 'scorer' still referenced in LongRangeForecasts.java
|
||||
[x] lambda syntax present
|
||||
[i] main class differs from reference solution (may still be a valid variant)
|
||||
Reference in New Issue
Block a user