Как я могу проверить строку в (список ( пара строк и (список строк)))?

#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 and False . 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