Соответствующие шаблоны для списка кортежей - PullRequest
1 голос
/ 27 мая 2019

Я пытаюсь написать fcn, который принимает список кортежей и выражений (я работаю над постфиксным выражением eval).Эта функция должна пройти через выражение и найти ту же букву в кортеже.Если это совпадение, то он возвращает значение int, соответствующее этой букве в кортеже.Когда я запустил приведенный ниже код, моя программа скомпилировалась и запустилась, но затем во время выполнения зависала.Что я сделал не так?

    let rec getVar ls exp = 

        match ls with
       |head::tl when (fst head) = exp -> printfn "%d" (snd head)
       | _  -> getVar ls exp



    let ls = [("a",5);("b",2);("c",3)]

    let exp = "ab+"
    getVar ls exp

Ответы [ 3 ]

3 голосов
/ 27 мая 2019

Ваше выражение match содержит последнее общее предложение (предложение _), которое просто вызывает getVar снова с теми же параметрами. Если условие when (fst head) = exp не выполняется, то код переходит к предложению catch-all, поэтому getVar вызывается снова с теми же параметрами, поэтому первое условие не выполняется, поэтому код переходит к предложению catch-all, поэтому getVar снова вызывается ... Это бесконечный цикл.

Что вы, вероятно, намеревались сделать, это снова вызвать getVar в хвосте списка, если ваше условие when не выполнено:

match ls with
|head::tail when (fst head) = exp -> printfn "%d" (snd head)
|head::tail -> getVar tail exp

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

2 голосов
/ 27 мая 2019

Ваше совпадение должно обрабатывать три случая.

  1. Пустой список -> вернуть некоторое значение по умолчанию (или единицу без побочных эффектов)
  2. Соответствие найдено -> вернуть значение или вызвать несколькопобочный эффект.
  3. Совпадение еще не найдено -> продолжить поиск в конце списка.

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

 let rec getVar ls exp =
     match ls with
     |[] -> None
     |head::tail when (fst head) = exp -> Some <| sprintf "%d" (snd head)
     |head::tail  -> getVar tail exp

 let ls = [("a",5);("b",2);("c",3)]

 let result1 = getVar ls "ab+"   // result = None
 let result2 = getVar ls "b"     // result = Some "2"
0 голосов
/ 02 июня 2019

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

Причина, по которой ваш код зависает, ясно объясняется в других ответах, поэтому я не буду повторяться здесь. Но в качестве хорошей практики, пожалуйста, не используйте | _ -> ..., если вы полностью не контролируете ситуацию. Как правило, мы должны явно писать все условия соответствия, чтобы, если мы что-то пропустили, компилятор предупредил нас. После этого, зная все условия, мы можем использовать | _ -> ..., если это действительно означает «условие покоя s ».

Вашу функцию getVar можно переписать так:

let rec getVar ls letter =
    match ls with
    | [] -> ()
    | head :: tail when fst head = letter -> printfn "%d" (snd head)
    | _ :: tail -> getVar tail letter

Я предлагаю вам изучить общие встроенные функции библиотеки ядра F #. Таким образом, вы можете использовать функцию List.tryFind в вашей задаче:

let getVar ls letter =
    ls |> List.tryFind (fun (key, value) ->
                 if key = letter then
                     printfn "%d" value
                     true
                 else false)

Чем больше вы можете использовать встроенных функций, тем меньше у вас ошибок.

...