2017-01-31 2 views
4

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

type volume = 
    | Litre of float 
    | Galon of float 
    | Bucket of float 
    | Bushel of float 

let list = [Litre(20.0);Litre(30.0);Galon(2.0);Bucket(5.0);Litre(5.0);Galon(3.0)]; 

Где выход должны выглядеть следующим образом:

[Litre(55.0);Galon(5.0);Bucket(5.0)] 

Я пришел с частью решения:

let rec sumSameTypes (list:volume list) = 
    match list with 
    | a::b::t -> if a.GetType() = b.GetType() then // and there is part where I don't know how to sum two of these elements 
    | [] -> failwith "EMPTY" 

ответ

7

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

В то время как GetType может (в данном случае) использоваться для проверки того, имеют ли два значения раздельного объединения один и тот же случай, это не особенно функциональный стиль. Вам нужно будет использовать сопоставление с образцом, чтобы проверить, что случай у вас есть, что также позволяет извлекать численное значение:

match a with 
| Litre(n) -> // Do something with 'n' 
// Add all the other cases here 

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

let rec sumSameTypes (list:volume list) : float * float * float * float = 
    match list with 
    | [] -> 
     // For empty list, we just have 0 of everything 
     0.0, 0.0, 0.0, 0.0 
    | x::xs -> 
     // For non-empty list, process the rest recrsively 
     // and pattern match on `x` to figure out which of 
     // the numbers you need to increment 

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

type Unit = Litre | Galon | Bushel | Bucket 
type Volume = { Amount : float; Unit : Unit } 

Это сделало бы его гораздо проще, так как вы могли бы использовать Map<Unit, float> как результат.

+0

Но все-таки я дон Не понимаю, как добавить значение n; '| x :: xs -> match x with' '| Литер (n) -> // и теперь что? ' –

+0

Вам нужно вызывать' sumSameTypes' рекурсивно на 'xs', чтобы получить итоговые литы, галоны и т. Д. Для остальной части списка, а затем добавить' n' в один возвращаемых номеров - в зависимости от типа тома, на который вы смотрите. –

+0

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

0

Вдохновленный классическая хвостовой рекурсивная функция суммы:

let sum lst = 
    let rec loop acc = function 
     | [] -> acc 
     | h::t -> loop (acc + h) t 
    loop 0.0 lst 

Вы можете использовать локальную хвостовую рекурсию функцию с 4 аккумуляторов (один для каждого типа):

let sumByType lst = 
    let rec loop litre gallon bucket bushel = function 
     | [] -> // return a new volume list built using the accumulators values 
     | h::t -> // recursive calls with modified accumulators according to h type 
    loop 0.0 0.0 0.0 0.0 lst