Неполный шаблон, соответствующий кортежу в F#

Я определяю точку

type TimeSeriesPoint<'T> = 
    { Time : DateTimeOffset
      Value : 'T }

и серия

type TimeSeries<'T> = TimeSeriesPoint<'T> list

где я предполагаю, что точки в этом списке упорядочены по времени.

Я пытаюсь сжать два временных ряда, где, как правило, у них будут точки с одинаковым временем, но некоторые из них могут отсутствовать.

Любая идея, почему я получаю предупреждение о неполных совпадениях с образцом в коде ниже?

let zip (series1 : TimeSeries<float>) (series2 : TimeSeries<float>) =
    let rec loop revAcc ser1 ser2 =
       match ser1, ser2 with
       | [], _ | _, [] -> List.rev revAcc
       | hd1::tl1, hd2::tl2 when hd1.Time = hd2.Time ->
           loop ({ Time = hd1.Time; Value = (hd1.Value, hd2.Value) }::revAcc) tl1 tl2
       | hd1::tl1, hd2::tl2 when hd1.Time < hd2.Time ->
           loop revAcc tl1 ser2
       | hd1::tl1, hd2::tl2 when hd1.Time > hd2.Time ->
           loop revAcc ser1 tl2
    loop [] series1 series2

Если я напишу это так, я не получу предупреждения, но рекурсивен ли он?

let zip' (series1 : TimeSeries<float>) (series2 : TimeSeries<float>) =
    let rec loop revAcc ser1 ser2 =
       match ser1, ser2 with
       | [], _ | _, [] -> List.rev revAcc
       | hd1::tl1, hd2::tl2 ->
           if hd1.Time = hd2.Time then
               loop ({ Time = hd1.Time; Value = (hd1.Value, hd2.Value) }::revAcc) tl1 tl2
           elif hd1.Time < hd2.Time then
               loop revAcc tl1 ser2
           else 
               loop revAcc ser1 tl2
    loop [] series1 series2

2 ответа

Решение

Вообще, это анти-паттерн иметь when охранник в последнем паттерне.

В zip, вы можете достичь того же эффекта, что и zip' делает, удалив лишнюю охрану:

let zip (series1: TimeSeries<float>) (series2: TimeSeries<float>) =
    let rec loop revAcc ser1 ser2 =
       match ser1, ser2 with
       | [], _ | _, [] -> List.rev revAcc
       | hd1::tl1, hd2::tl2 when hd1.Time = hd2.Time ->
           loop ({ Time = hd1.Time; Value = (hd1.Value, hd2.Value) }::revAcc) tl1 tl2
       | hd1::tl1, hd2::tl2 when hd1.Time < hd2.Time ->
           loop revAcc tl1 ser2
       | hd1::tl1, hd2::tl2 ->
           loop revAcc ser1 tl2
    loop [] series1 series2

Обе эти функции являются хвостовыми рекурсивными, так как после рекурсивного вызова нет никакой дополнительной работы loop,

В первом случае компилятор просто видит охранников и не достаточно умен, чтобы рассуждать о том, когда они применяются / не применяются - это можно исправить, удалив последний элемент защиты where.

Во-вторых, я бы предположил, что это хвостовая рекурсия, но лучшее, что нужно сделать в этих случаях, - это протестировать с большим списком ввода и посмотреть, не произойдет ли сбой.

Другие вопросы по тегам