2015-07-07 2 views
2

Я хотел бы получить последний индекс элемента в списке, соответствующем предикату. Я знаю, что я могу использовать следующий код для массива, но мне интересно, если есть способ сделать это для списка (без преобразования в массив и используя следующую функцию):Как найти индекс последнего элемента в списке, совпадающем с предикатом?

let tryFindLastIndex f (arr: 'a []) = 
    let rec loop n (arr: 'a []) = 
     match n with 
     | -1 -> None 
     | n -> 
      if f arr.[n] then Some n 
      else loop (n-1) arr 
    loop (arr.Length - 1) arr 
+0

Код будет очень похожим, но с совпадением рисунков на лисе t, а не индексировать его. Вы пробовали что-нибудь в этом направлении? С какими проблемами вы сталкиваетесь? –

+0

(Извините, я не понимал, что вы повторяете массив _backwards_. Это делает его более интересным!) –

ответ

3

list в F # это связанный список, который вы не можете легко перебирать в последнем -> в первом направлении. Из-за этого вам нужно выполнить итерацию с самого начала и сохранить следы как текущего индекса, так и индекса последнего элемента, который соответствует предикату.

Вы по-прежнему используете локальную рекурсивную функцию для этого.

let tryFindLastIndex f source = 
    let rec loop index lastFoundIndex source = 
     let newIndex = index+1 
     match source with 
     | [] -> lastFoundIndex 
     | head :: tail -> 
      if f head then loop newIndex (Some index) tail 
      else loop newIndex lastFoundIndex tail 
    loop 0 None source 
4

Я бы подойти к этой задаче с идиоматических использования библиотечных функций и комбинаторов:

let tryFindLastIndex f ls = 
    match ls |> (List.rev >> List.tryFindIndex f) with 
    | None -> None 
    | Some n -> Some (ls.Length - n - 1) 

EDIT: для тех, кто более озабочен производительности, то же самое может быть достигнуто одним списком пересекаются с той же ясностью намерения, все еще используя библиотечные функции и комбинаторы:

let tryFindLastIndex f ls = 
    match (ls |> List.fold (fun (i,r) l -> if f l then (i+1,i::r) else (i+1,r)) (0,[]) |> snd) with 
    | [] -> None 
    | x -> Some (x.Head) 
+0

Для коллекции требуется как минимум 2 полных прохода (для 'List.rev' и' ls.length') + в худшем случае, когда никакие элементы не соответствуют условию третьего ('List.tryFindIndex'). – MarcinJuraszek

+0

@MarcinJuraszek: Я не вижу никаких признаков того, что вопрос имеет проблемы с производительностью, так зачем беспокоиться? Если производительность является проблемой, может применяться другой, но все же идиоматический подход - см. Отредактированный ответ. –

Смежные вопросы