избегание циклов for в F#

#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)
  

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

  1. aa- со значением 30, aa со значением 10, aa- со значением 20, поэтому max равен 30, а min равен 10
  2. b1 со значением 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