无法索引递归构造的列表:getN'函数输出异常求助
Let's break down what's happening with your getN' function and how to fix the issues with indexing and unexpected output.
The Root of the Problem
Your current implementation uses lazy recursion to build the list:
getN' : (Double -> Double -> Double) -> Double -> Double -> Double -> Int -> List Double getN' f dt t0 y0 0 = [] getN' f dt t0 y0 n = rk4' :: getN' f dt (t0+dt) rk4' (n-1) where rk4' = rk4 f dt t0 y0
Haskell's lazy evaluation means it doesn't compute the entire list upfront. When you print the result, only the first element is evaluated, and the rest stays as unevaluated thunks (the getN' f 0.1 0.1 1.00500624... part you see). This makes the list look incomplete, and while indexing with (!!) technically works (it will compute elements on-demand), it's not ideal if you need the full list to be evaluated and displayed properly.
Additionally, your current function generates a list of n elements starting with the first RK4 step result (at t0+dt), not including the initial y0. If you meant to include y0 as the first element, we can adjust that too.
Solution 1: Tail-Recursive Accumulator for Strict, Ordered Lists
To get a fully evaluated list that displays correctly and is easier to work with, use a tail-recursive helper function with an accumulator. This builds the list in reverse order first, then reverses it to get the correct sequence:
getN' : (Double -> Double -> Double) -> Double -> Double -> Double -> Int -> List Double getN' f dt t0 y0 n = reverse $ go t0 y0 n [] where -- Base case: when n reaches 0, return the accumulated list go _ _ 0 acc = acc -- Recursive step: compute next y, add to accumulator, move to next time step go t y k acc = let yNext = rk4 f dt t y in go (t + dt) yNext (k - 1) (yNext : acc)
This approach forces evaluation of each yNext as we build the accumulator, so when you call getN' f 0.1 0 1 100, the result will be a fully computed list of 100 RK4 step results, displayed as [1.00500624..., 1.01002502..., ...] instead of the recursive thunk.
Solution 2: Including the Initial Value (If Needed)
If you wanted the list to start with the initial y0 (making it n+1 elements total), adjust the helper function to include y0 in the accumulator from the start:
getNWithInitial' : (Double -> Double -> Double) -> Double -> Double -> Double -> Int -> List Double getNWithInitial' f dt t0 y0 n = reverse $ go t0 y0 n [y0] where go _ _ 0 acc = acc go t y k acc = let yNext = rk4 f dt t y in go (t + dt) yNext (k - 1) (yNext : acc)
Now getNWithInitial' f 0.1 0 1 100 will return 101 elements: starting with 1.0 (the initial y0), followed by 100 RK4 step results.
Testing Indexing
With either of these implementations, indexing works smoothly. For example:
-- Get the 50th element (0-indexed) of the 100-step list result = getN' f 0.1 0 1 100 fiftiethElement = result !! 49
Since the list is fully evaluated, this will retrieve the value immediately without deferred computation.
内容的提问来源于stack exchange,提问作者corazza

