Как искать в списке элементы кортежа? - PullRequest
0 голосов
/ 20 мая 2019

Для моего задания я должен взять список парочек людей на свадьбе, которые не могут сидеть рядом друг с другом.Затем сравните это со списком людей за столом.Если какие-либо два человека в одном кортеже находятся в списке таблиц, это должно быть false.В противном случае правда.Это мой первый раз код на F #, поэтому синтаксис меня здесь убивает.

let isValidTable (cantSit:(string*string) list) (people: string list) =

    let truth = true;
    let rec matchPeople cantSit person1 person2= 
        match cantSit with
        | [] -> None
        | head :: tail ->
            let (person1,person2) = head 
            if ((List.exists(fun names -> names = person1) people) && (List.exists(fun names2 -> names2 = person2) people)) then
                let result2 = false
            else 
                matchPeople tail fst snd;;
                let result = true;;
    matchPeople cantSit fst snd;;


let x = [("Eric", "Mark"); ("Anna", "Maya"); ("Beth", "Hope")];; 
let weddingList = ["Eric"; "Anna"; "Beth"]
let validOrNah = isValidTable x weddingList;;
printf("\n%O") validOrNah;;

Проблема для меня заключается в том, что я продолжаю получать ошибки типа «конструктор matchPeople не определен» или «пусть не завершен».Буду признателен за любую помощь, спасибо!

Раньше: у меня был

if(first person is in list and second person is in list)
then Some false
else recursive statement;

Этот код скомпилирован без ошибок, но напечатан только с нулевым значением.Я знаю, что переменные неизменны в F #, что делает это сложным.

1 Ответ

7 голосов
/ 20 мая 2019

В вашем коде много проблем, вызванных тем, что вы не знакомы с F #.Я постараюсь объяснить это построчно и попытаюсь объяснить, что вы еще не поняли.

let isValidTable (cantSit:(string*string) list) (people: string list) =

Это нормально.

    let truth = true;

Нет необходимости в этом назначении ввсе, так как вы никогда не используете имя truth где-либо еще в вашем коде.И если вам нужно его использовать, вы можете просто заменить его константой true, и она будет читаться лучше.Давайте полностью удалим эту строку.

Кстати, в конце строки F # нет необходимости использовать точки с запятой в конце строк, в отличие от языков, подобных C.Двойная точка с запятой используется только в интерпретаторе F # Interactive, чтобы сообщить интерпретатору «Я закончил вводить это выражение».Это позволяет разбить выражение на несколько строк, и интерпретатору не нужно угадывать, когда вы закончите (потому что многие частичные выражения в F # могут выглядеть завершенными, поэтому необходимо явное завершение выражения).Я не буду упоминать об этом каждый раз, когда появляется точка с запятой, но вы можете удалить все точки с запятой (и двойные точки с запятой) в конце ваших строк.Единственная точка с запятой * , необходимая в F #, находится между элементами в списке, например x или weddingList.

В следующей строке.

    let rec matchPeople cantSit person1 person2=

Это выглядит отлично, но на самом деле вам не нужны параметры person1 и person2.Я предполагаю, что они есть в списке параметров, потому что вы думаете, что вам нужно объявить переменные перед их созданием, но F # работает совсем не так.Когда вы позже напишете let (person1, person2) = head в функции, переменные person1 и person2 будут созданы прямо здесь, и нет необходимости иметь их в качестве параметров функции.Таким образом, вы можете удалить их, и ваше определение функции станет let rec matchPeople cantSit =

        match cantSit with

Это нормально.

        | [] -> None

Это небольшая ошибка.В другом месте вы выглядите так, как будто хотите вернуть логическое значение, но здесь вы вместо этого возвращаете опцию.В F # все ветви match и / или if...else должны возвращать один и тот же тип.Ваша isValidTable функция явно предназначена для возврата логического значения, как и matchPeople, поэтому это также должно быть логическое значение.Вопрос в том, должна ли эта строка возвращать false или true?Чтобы ответить на этот вопрос, подумайте, что означает пустой список cantSit в семантике вашей проблемной области.Это означало бы, что нет никого, кто не может сидеть друг с другом, поэтому список мест действителен независимо от того, кто за столом.Или, конечно, это также может означать, что вы достигли конца списка cantSit с помощью нескольких рекурсивных вызовов, и в этом случае возвращаемое здесь значение будет значением, которое вы вернете в конце последнего рекурсивного вызова.И снова, возвращение true - это то, что вы хотите, потому что, если бы вы ранее нашли недопустимую сидячую пару, вы бы немедленно вернули false и не сделали бы еще один рекурсивный вызов.Так что если вы дойдете до точки, где список cantSit пуст, то вы готовы вернуть true.

        | head :: tail ->

Это нормально.

            let (person1,person2) = head 

Этоэто не просто хорошо, это довольно хорошо.

            if ((List.exists(fun names -> names = person1) people) && (List.exists(fun names2 -> names2 = person2) people)) then

Это хорошо, но можно упростить.Здесь есть функция List.contains, которая делает то, что вы хотите здесь.Любой вызов типа List.exists (fun item -> item = value) itemList) может быть упрощен до List.contains item itemList.Таким образом, это станет if (List.contains person1 people) && (List.contains person2 people) then, что намного легче читать и понимать быстро.

                let result2 = false

Это неверно;присваивание let в F # не имеет значения, и, поскольку оно является последним выражением в блоке if, это означает, что блок if не будет иметь значения, если его условие окажется истинным.Вот почему вы получаете «незавершенные» ошибки: в F # * let присваивание никогда не может быть последним выражением блока кода .За ним всегда должно следовать выражение, которое имеет значение.То, что вы на самом деле пытаетесь сделать здесь, довольно ясно: вы хотите вернуть false, если оба человека в списке.Вы можете сделать это, просто написав false в этой строке;Я объясню немного больше здесь.

В F # if...else - это выражение , которое возвращает значение , а не утверждение, как в большинстве других языков. Таким образом, вы можете написать что-то вроде этого:

let n = 5
let s = if n % 2 = 0 then "Even" else "Odd"
printfn "%s" s  // Prints "Odd"

Здесь if...else - последняя строка одного регистра выражения match, поэтому его значением будет значение выражения match. А выражение match является последним выражением функции matchPeople, поэтому его значением будет возвращаемое значение функции. Таким образом, в случае, когда вы найдете подходящую пару, которая не может сидеть вместе (истинная ветвь этого if...else выражения), тогда вам просто нужно иметь строку, говорящую false, и это будет возвращаемое значение функция, если он попадает в эту ветку.

Переходя к следующей строке.

            else 

Это нормально, очевидно.

                matchPeople tail fst snd;;

Это нормально, если вы удалите fst и snd (поскольку мы изменили сигнатуру нашей функции, так что matchPeople теперь принимает только один аргумент), и удалите точки с запятой, как упоминалось ранее.

                let result = true;;

Тот же комментарий, что и для более ранней строки let result2 = false: присвоение let никогда не может быть последней строкой блока кода в F #. Здесь вы хотите, чтобы результат рекурсивного вызова matchPeople был конечным результатом вашего «внешнего» уровня рекурсии. Вы можете сделать это, просто удалив эту строку let result = true, чтобы вызов matchPeople был последней строкой блока else. Это означает, что его результат будет результатом блока else, а поскольку выражение if...else является последним выражением этого случая match, рекурсивный вызов будет последним выражением оператора match. А поскольку оператор match является последним выражением функции matchPeople, его результат также будет результатом всей функции (если код достигает ветви else). Это означает, что этот рекурсивный вызов находится в хвостовой позиции , что является важной концепцией позже: вызов находится в хвостовой позиции, если его результат будет результатом всей функции. Вызов в хвостовой позиции обычно известен как короткий вызов. Я не буду вдаваться в подробности о хвостовых вызовах здесь, за исключением того, что скажу, что хвостовой вызов может быть оптимизирован компилятором так, чтобы он никогда не вызывал ошибку переполнения стека, независимо от того, сколько раз вы проходите рекурсивный вызов. Сейчас мы отложим хвостовые вызовы и продолжим изучать остальную часть вашего кода:

    matchPeople cantSit fst snd;;

Как и в случае другого вызова, просто удалите параметры fst и snd (и двойную точку с запятой), и это будет хорошо.

let x = [("Eric", "Mark"); ("Anna", "Maya"); ("Beth", "Hope")];; 
let weddingList = ["Eric"; "Anna"; "Beth"]
let validOrNah = isValidTable x weddingList;;
printf("\n%O") validOrNah;;

Все это хорошо, если вы удалите ненужные двойные точки с запятой. Вероятно, я бы написал printfn "%O" validOrNah в последней строке, но это личное предпочтение: мне нравится печатать новую строку в конце моего вывода, а не в начале (printfn печатает новую строку после того, что вы просите его напечатать, в то время как printf без завершающего символа n в имени функции не печатает завершающий символ новой строки). Но то, что вы здесь написали, прекрасно.

Внося все эти изменения, вот во что превращается ваш код:

let isValidTable (cantSit:(string*string) list) (people: string list) =

    let rec matchPeople cantSit =
        match cantSit with
        | [] -> true
        | head :: tail ->
            let (person1,person2) = head 
            if (List.contains person1 people) && (List.contains person2 people) then
                false
            else 
                matchPeople tail
    matchPeople cantSit

let x = [("Eric", "Mark"); ("Anna", "Maya"); ("Beth", "Hope")]
let weddingList = ["Eric"; "Anna"; "Beth"]
let validOrNah = isValidTable x weddingList
printfn "%O" validOrNah

Я не внес никаких изменений в вашу логику, поскольку она правильная (хорошо сделано!), Поэтому, как только вы сделаете эти исправления синтаксиса, которые я предложил, он должен запуститься и вывести правильные результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...