Вставьте функцию используя foldl/foldr
Я работал над отдельной функцией, которая возвращает список, который вставляет элемент x после каждого k элементов списка l (считая от конца списка). Например, отдельный (1, 0, [1,2,3,4]) должен возвращать [1,0,2,0,3,0,4]. Я закончил функцию, и она работает следующим образом:
fun separate (k: int, x: 'a, l: 'a list) : 'a list =
let
fun kinsert [] _ = []
| kinsert ls 0 = x::(kinsert ls k)
| kinsert (l::ls) i = l::(kinsert ls (i-1))
in
List.rev (kinsert (List.rev l) k)
end
Сейчас я пытаюсь упростить функцию, используя foldl/foldr без какой-либо рекурсии, но я не могу заставить ее работать правильно. Любые советы / предложения о том, как подойти к этому? Благодарю вас!
1 ответ
Это более или менее мысли, которые у меня были, когда я пытался написать функцию, используя foldl
/foldr
:
foldl
/foldr
абстрагирует рекурсию списка от логики, составляющей конечный результат.Начните с наброска функции, которая имеет структуру, очень похожую на исходную программу, но где
foldr
используется иkinsert
вместо того, чтобы быть рекурсивной функцией, это функция, даннаяfoldr
:fun separate (k, x, L) = let fun kinsert (y, ys) = ... in foldr kinsert [] L end
Это не строго необходимо;
kinsert
может также быть анонимнымВы используете внутреннюю вспомогательную функцию
kinsert
потому что вам нужна копияk
(i
) что вы постепенно уменьшаете и возвращаетесь кk
каждый раз, когда он достигает 0. Так что в то время как список, которыйkinsert
выплевывает эквивалентно накопленной переменной сгиба,i
временно накапливается (и иногда сбрасывается) почти таким же образом.+ Изменить
kinsert
Накапливается переменная, чтобы освободить место дляi
:fun separate (k, x, L) = let fun kinsert (y, (i, xs)) = ... in foldr kinsert (?, []) L end
Теперь результат сгиба становится
'a * 'a list
, что вызывает две проблемы: 1) Мы действительно только хотели накопитьi
временно, но это часть окончательного результата. Это можно обойти, отбросив его с помощью#2 (foldr ...)
, 2) Если результат теперь кортеж, я не уверен, что поставить в качестве первогоi
на месте?
,поскольку
kinsert
это отдельное объявление функции, вы можете использовать сопоставление с образцом и несколько тел функций:fun separate (k, x, L) = let fun kinsert (y, (0, ys)) = ... | kinsert (y, (i, ys)) = ... in ... foldr kinsert ... L end
Ваш оригинал
kinsert
отклоняется от шаблона рекурсии, который складка выполняет одним способом: в среднем шаблоне, когдаi
Матчи0
вы не отрубаете элементls
, который в противном случае вы бы заставили. Так что ваши0
чехол будет немного отличаться от оригинала; вы, вероятно, столкнетесь с ошибкой.Помни что
foldr
фактически посещает последний элемент списка первым, после чегоi
будет иметь начальное значение, где с оригиналомkinsert
, начальное значение дляi
будет, когда вы на первом элементе.В зависимости от того, используете ли вы
foldl
или жеfoldr
вы столкнетесь с разными проблемами:foldl
перевернет ваш список, но адрес элементов в правильном порядке.foldr
сохранит правильный порядок в списке, но при создании другого результатаk
не делит длинуL
...На этом этапе рассмотрите возможность использования
foldl
и вместо этого переверните список:fun separate (k, x, L) = let fun kinsert (y, (?, ys)) = ... | kinsert (y, (i, ys)) = ... in rev (... foldl kinsert ... L) end
В противном случае вы начнете замечать, что
separate (2, 0, [1,2,3,4,5])
должно дать[1,2,0,3,4,0,5]
и не[1,0,2,3,0,5]
,