#for-loop #f# #functional-programming
#цикл for #f# #функциональное программирование
Вопрос:
У меня есть список строк и чисел, подобных следующему
let stringsAndNums = [("aa-",20); ("b1",20); ("aa",10); ("b12",10); (" aa-",30)]
Мне нужно разделить список на группы строк, которые включены одна в другую.
Для каждой из вышеуказанных групп я должен найти минимальное и максимальное значение.
Это то, что я пытался сделать: это работает, но я не думаю, что это идиоматический F #, и я думаю, мне следует избегать циклов for.
for tup in stringsAndNums do
let subject, value = tup
let related =
stringsAndNums |> List.filter(
fun o ->
let osubject, ovalue = o;
osubject.Contains(subject) || subject.Contains(osubject);
)
let relvalues = related |> List.map(fun o ->
let osubject, ovalue = o;
ovalue
)
let min = (relvalues |> List.min)
let max = (relvalues |> List.max)
printfn "%A" (subject, value, min, max, (max - min))
Кроме того, как я могу определить функцию, возвращающую список результатов кортежа вместо их печати?
Желаемый результат.
Результаты, которые я получаю, выглядят нормально
("aa-", 20, 10, 30, 20)
("b1", 20, 10, 20, 10)
("aa", 10, 10, 30, 20)
("b12", 10, 10, 20, 10)
(" aa-", 30, 10, 30, 20)
Фактически две группы в этом случае формируются
aa-
со значением 30,aa
со значением 10,aa-
со значением 20, поэтому max равен 30, а min равен 10b1
со значением 20,b12
со значением 10
Мое решение
Что мне теперь удалось сделать: цикла for больше нет, но действительно ли этот код функционален?
let results =
stringsAndNums |> List.map(fun tup ->
//for tup in stringsAndNums do
let subject, value = tup
let related =
stringsAndNums |> List.filter(
fun o ->
let osubject, ovalue = o;
osubject.Contains(subject) || subject.Contains(osubject);
)
//for reltup in related do
let relvalues = related |> List.map(fun o ->
let osubject, ovalue = o;
ovalue
)
let min = (relvalues |> List.min)
let max = (relvalues |> List.max)
printfn "%A" (subject, value, min, max, (max - min))
)
for result in results do
printf "%A" result
Комментарии:
1. Не могли бы вы показать, каким будет ваш желаемый результат?
Ответ №1:
Вот как я бы это написал.
let subjectValues = [("aa-",20); ("b1",20); ("aa",10); ("b12",10); (" aa-",30)]
let getRelated (subject, value) =
let relValues =
subjectValues
|> List.choose (fun (s, v) ->
if s.Contains(subject) || subject.Contains(s)
then Some v
else None)
let min = relValues |> List.min
let max = relValues |> List.max
(subject, value, min, max, (max - min))
let results = subjectValues |> List.map getRelated
printfn "%A" results
-
List.map
Функция не может возвращатьunit
(что-то вродеvoid
). Это приводило к побочному эффекту печати вместо значения. -
Сопоставление шаблонов кортежей может быть выполнено непосредственно в параметре функции, а не в отдельном
let
. -
Обычно я стараюсь написать функцию, которая работает с одним элементом, а затем при необходимости использовать ее с List.map, поскольку я нахожу это более чистым и гибким.
-
Я заменил
List.map ... List.filter
наList.choose
, который просто выполняет и то, и другое за один шаг. Это привело к удалению промежуточного трудноименуемогоlet
значения. -
Я переименовал некоторые значения, чтобы придать им больше смысла, я надеюсь.
Ответ №2:
Поскольку я не понимаю, чего вы пытаетесь достичь, мое решение — это просто небольшая очистка
let stringsAndNums = [("aa-",20); ("b1",20); ("aa",10); ("b12",10); (" aa-",30)]
let results =
let split (subject, value) =
let related =
//first I created a named function for the filter expression
let filtering (osubject:string, _) = osubject.Contains(subject) || subject.Contains(osubject)
stringsAndNums |> List.filter filtering
//accessing the 2 first items of a tuple can be done via fst, snd
let relvalues = related |> List.map snd
let min = (relvalues |> List.min)
let max = (relvalues |> List.max)
//I assume you wanted to return that tuple so away with the printf
(subject, value, min, max, (max - min))
stringsAndNums |> List.map split
for result in results do
//and lastly use printfn (n-for newline) so the printing is nicer
printfn "%A" result