#haskell
#haskell
Вопрос:
У меня есть данные под названием «Образец», определенные как
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
Затем я создаю список [Sample] под названием «Samples», скажем
[Test1 ("Sample1",[["works","running"]]), Test2 ("Sample2", []),
Test1 ("Sample3", [["aborts"]] ]
Теперь мне нужно проверить, присутствует ли «прерывания» в разделе «Sample3». У меня есть общее
представление о том, как это сделать, но я не уверен, как это реализовать в Haskell.
Моя идея
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples =
Я могу вызвать это с помощью:
check "Sample3" ["aborts"] Samples
Но как мне реализовать эту функцию.
Я решил так, но я ищу лучшую версию.
Моя версия:
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples =
if( "True" `elem` final )
then True
else False
where
final = [x | sample <- samples,
x <- [ case sample of
Test1 (name,res) =
if name == samplename amp;amp; (result `elem` res)
then "True"
else "False"
Test2 (name, res) = "False"]]
Комментарии:
1. Я не понимаю
[String]
, для чего используется аргумент («результат»).2. результатом является список, содержащий строку «прерывания»
3. Обратите внимание на стиль кода.
if x then True else False
это то же самое, что просто писатьx
. Не рекомендуется использовать строки"True"
и"False"
. В этом случае вы можете полностью удалить их и просто использоватьTrue
andFalse
.elem True xs
то жеor xs
самое, что и .
Ответ №1:
Вот моя версия решения. Но я не думаю, что ваши данные представляют какую-либо реальную проблему, вы можете легко использовать Map, если хотите сохранить что-то вроде [Sample], который в основном представляет собой список пар ключ-значение. Ну, идея моего решения также вдохновлена Map . Я написал lookup'
функцию, аналогичную lookup, которая по заданному ключу возвращает значение. тогда написание функции rest является тривиальным. Аналогично вашему подходу, но менее беспорядочно.
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
deriving Show
samples = [ Test1 ("Sample1",[["works","running"]]), Test2 ("Sample2", []), Test1 ("Sample3", [["aborts"]]) ]
fromSample :: Sample -> (String,[[String]])
fromSample (Test1 x) = x
fromSample (Test2 x) = x
lookup' :: String -> [Sample] -> Maybe [[String]]
lookup' str [] = Nothing
lookup' str (x:xs) | fst pair == str = Just $ snd pair
| otherwise = lookup' str xs
where pair = fromSample x
check :: String -> [String] -> [Sample] -> Bool
check sample str xs = case lookup' sample xs of
Nothing -> False
Just list -> str `elem` list
Ответ №2:
Если мы подумаем о том, что проверка должна выполняться на высоком уровне, мы знаем, что она должна проверять каждый элемент выборок, чтобы увидеть, содержит ли тест «Sample3» какие-либо (или все, я не понимаю, что вы имеете в виду) строки, результат которых присутствует.
Итак, мы знаем, что нам нужна рекурсия, и мы можем начать с создания общей схемы функции:
check :: String -> [String] -> [Sample] -> Bool
check samplename result [] = False
check samplename result (x:xs) = ...
Поэтому, когда список пуст, совпадений не может быть, поэтому мы можем немедленно вернуть false . В рекурсивном случае мы знаем, что нам нужно проверить x и, если совпадение не найдено, продолжить проверку xs. Один из возможных способов сделать это — с помощью вспомогательной функции check’ (вы также можете просто встроить проверку’).
check samplename result (x:xs) = check' x || check samplename result xs
where check' ...
хорошо, так что же делает check’? Он проверяет образец типа данных, чтобы увидеть, встречаются ли какие-либо совпадения. Мы знаем, что в примере есть два конструктора, Test1 и Test2, поэтому проверка’ должна выглядеть так
check' :: Sample -> Bool
check' (Test1 (name, values)) = ...
check' (Test2 (name, values)) = ...
Первое, что нам нужно сделать, это проверить значение name, чтобы увидеть, соответствует ли оно samplename . Мы можем сделать это легко, используя guards
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = ...
check' (Test2 (name, values)) | name == samplename = ...
check' _ = False
Поскольку check’ является дочерней функцией check , переменные, определенные в check, находятся в области видимости, поэтому мы можем просто ссылаться на них. Добавляется новый регистр для обработки события, когда имена не совпадают.
Хорошо, теперь идея состоит в том, чтобы проверить, встречаются ли какие-либо (или все) значения в результате в значениях. К счастью, в prelude есть функция, которую мы можем использовать для этого.
elem :: Eq a => a -> [a] -> Bool
Теперь функция становится
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
Таким образом, полная функция:
check :: String -> [String] -> [Sample] -> Bool
check samplename result [] = False
check samplename result (x:xs) = check' x || check samplename result xs
where check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
Проверка каждого элемента списка с помощью предиката настолько распространена, что в prelude есть стандартные функции для этого. одним из возможных способов определения проверки является использование функций or и map .
Это, как правило, приводит к менее эффективной функции, хотя:
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
Функция может быть дополнительно упрощена путем адаптации альтернативной структуры для типа данных, например
type ID = Int
type Name = String
type Values = [[String]]
data Sample = Test ID Name Values
Затем функция становится
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test _ name values) | name == samplename = result `elem` values
check' _ = False
Наконец, поскольку результатом проверки’является Bool, а guards также являются Bool, мы можем реорганизовать check’ в более простую форму, для которой не нужен случай провала
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test _ name values) = name == samplename amp;amp; result `elem` values
Этот шаблон ** или (map .. ..) ** настолько распространен, что опять же есть функция prelude, которая делает это. затем проверка может быть дополнительно упрощена до
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = any check' samples
where check' :: Sample -> Bool
check' (Test _ name values) = name == samplename amp;amp; result `elem` values
Ответ №3:
Это должно сработать:
data Sample = Test1 String [[String]]
| Test2 String [[String]]
sampleName :: Sample -> String
sampleName (Test1 name _) = name
sampleName (Test2 name _) = name
getData :: Sample -> [[String]]
getData (Test1 _ samples) = samples
getData (Test2 _ samples) = samples
check :: String -> [String] -> [Sample] -> Bool
check name needle samples = any doCheck samples
where
doCheck s = if sampleName s == name
then needle `elem` getData s
else False
Если предполагается, что все имена различны, вы можете прервать поиск быстрее:
check2 :: String -> [String] -> [Sample] -> Bool
check2 name needle samples = go samples
where
go [] = False
go (s:ss) = if sampleName s == name
then needle `elem` getData s
else go ss
Тестирование:
*Main> check "Sample3" ["aborts"] tst
True
Комментарии к вашей версии:
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
Здесь вам не нужны кортежи (см. Мой код).
if( "True" `elem` final ) then True else False
В общем, if expr then True else False
всегда можно заменить на expr
. И почему вы используете строки как логические значения?
final = [x | sample <- samples,
x <- [ case sample of
Test1 (name,res) -> if name == samplename
amp;amp; (result `elem` res)
then "True" else "False"
Test2 (name, res) -> "False"]]
Это можно переписать без понимания списка, используя any
:
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples = any doCheck samples
where
doCheck (Test1 n s) = n == samplename amp;amp; result `elem` s
doCheck _ = False