Доступ к определенным элементам в списке кортежей в f # без библиотек - PullRequest
0 голосов
/ 01 октября 2018

У меня есть список кортежей с тремя различными элементами, например:

[(a0:string, b0:string, c0:int); (a1, b1, c1); (and so on...)].

Мне нужно создать функцию, которая принимает этот список и «имя» в форме a и выдает список всех b s, где имя соответствует a в кортеже.Но я не уверен, как все перебрать и сопоставить.

input: function name tuplelist
output: [b0 if a0 = name; b1 if a1 = name; b2 if a2 = name]

Я также не могу использовать библиотеки !!!

Ответы [ 3 ]

0 голосов
/ 01 октября 2018

При написании кода в функциональном стиле вы часто будете использовать рекурсию, если не явно, то неявно, поскольку большинство функций списка / массива / последовательности используют рекурсию под капотом.

В F # вам необходимо явно указатьчто функция является рекурсивной, поэтому созданная вами функция будет использовать синтаксис let rec в своем определении.Учитывая ваши требования, ваша функция, вероятно, будет выглядеть следующим образом:

let rec myFilter (name: string) (input: (string * string * int) list) =
    ...

Для этого класса задач, когда вы рекурсивно перебираете список, вы обычно используете сопоставление с образцом, чтобы проверить, находитесь ли вы в концеlist, и если да, вернуть пустой список.

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        ...

Теперь вам нужно написать сопоставление с шаблоном, которое проверяет первый элемент в кортеже на соответствие предоставленному имени.Вы можете использовать сопоставление с образцом в начале списка и синтаксис F # * when, чтобы деконструировать заголовок списка для сравнения

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        | ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
        ...

Этот второй случай совпадает, когда a соответствует запрашиваемому имени.Когда он совпадет, он вернет новый список, в котором b является главой списка, а затем возьмет оставшуюся часть списка кортежей и рекурсивно вызовет myFilter.Вот как вы рекурсивно перебираете список.

У нас есть еще один случай для проверки: если мы не найдем совпадения, мы хотим продолжать проходить по списку, не собирая b.Это можно выразить, откинув голову и рекурсивно позвонив myFilter, отправив только остальные наборы.

let rec myFilter (name: string) (input: (string * string * int) list) =
        match input with
        | [] -> []
        | ((a, b, _) :: rest) when a = name -> b :: myFilter name rest
        | (_ :: rest) -> myFilter name rest

Позвонив на myFilter "a" [("a","x",3);("b","y",4);("a","z",5)], вы получите ["x"; "z"], как и ожидалось.

0 голосов
/ 01 октября 2018

Эта задача требует от вас написать собственную функцию обработки рекурсивного списка.Я смог реализовать это, используя две подфункции.

Хотя ответы @GeneBelitski и @ChadGilbert отлично подходят для обучения, они не являются хвостово-рекурсивными, поэтому я добавлю свою собственную.

Подфункция aux принимает аккумулятор и список для обработки и сопоставляется с формой списка.Если он пуст, он возвращает результат, накопленный до сих пор.Если это 3-кортеж с хвостом, он сравнивает первый элемент с name и, если они равны, продолжает работать сам, добавляя второй элемент к накопленным результатам и хвост исходного списка, в противном случае просторезультаты, накопленные до сих пор, и хвост исходного списка.

Поскольку этот способ накопления результатов инвертирует порядок полученного списка, нам необходимо обратить его вспять, прежде чем возвращать;это то, что делает подфункция rev, и, как вы видите, код выглядит почти идентично aux, добавляя элементы к результатам, накопленным до сих пор, но без какого-либо сравнения или обработки.

let filter_b name lst =
    let rec aux acc = function
        | [] -> acc
        | (a : string, b : string, c : int) :: tl ->
            if a = name
            then aux (b :: acc) tl
            else aux acc tl
    let rec rev acc = function
        | [] -> acc
        | hd :: tl -> rev (hd :: acc) tl
    lst
    |> aux []   // process list with aux function
    |> rev []   // reverse resulting list
0 голосов
/ 01 октября 2018

Мощное сопоставление с образцом F # и рекурсия вместе с выводом типа легко компенсируют ограничение отбрасывания библиотек.

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

Подойдет что-то вроде следующего:

let filter name (tupleList:(string*string*int) list) =
    let checkElement name = function | (a,b,c) -> if a = name then Some b else None
    let rec mapList name inputList outputList =
        match inputList with
        | [] -> outputList
        | h::t -> let filter = checkElement name h
                  mapList name t (if filter = None then outputList else (outputList @ [filter.Value]))
    mapList name tupleList [] 

Здесь checkElementявляется функцией отображения, которая принимает name и кортеж (a, b, c) и возвращает значение параметра либо Some b, если a = name, либо None, если нет.

Рекурсивная функция mapListна каждом шаге работает с необработанной частью inputList кортежей и outputList, накапливая на каждом шаге рекурсии части только из согласованных элементов.На каждом шаге рекурсии он проверяет, является ли inputList пустым.Если да, то мы закончили и пришло время вернуть накопленный результат, в противном случае мы отсекаем элемент head от inputList и применяем к нему функцию отображения, изменяя накопленный список, если это так.Затем мы делаем следующий шаг рекурсии для хвоста inputList и аккумулятора.

...