简体   繁体   中英

Haskell custom data types

I have struggled with this for hours, and I cannot figure it out.

module Main where
import Data.List
import Data.Function

type Raw = (String, String)

icards = [("the", "le"),("savage", "violent"),("work", "travail"),("wild", "sauvage"),
("chance", "occasion"),("than a", "qu'un"),("expensive.", "cher."),("saves", "en 
vaut"),("time", "temps"),("in", "<`a>"), ("worse", "pire"),("{", "{"),("A", "Un"),
("stitch", "point"),("crime;", "crime,"),("a", "une"),("nine.", "cent."),("It's", 
"C'est"),("all","tout"),("rare", "rare"),("you", "vous"),("Abandon","Abandonnez"),
("stash", "planquer"),("Everything", "Tout!ce!qui!est"),("who enter.", "qui entrez."),
("Zazie", "Zazie"),("is", "est"),("cat", "chat"),("it's", "c'est"),("raisin", "raisin 
sec"),("hope,", "espoir,"),("mistake.", "faute."),("luck", "chance"),("blueberry", 
"myrtille"),("I!took", "J'ai pris"),("that", "qui"),("a!chance.", "des risques."),
("drink", "boisson"),("Live", "Vivre"),("regrets.", "regrets."),("stone", "pierre"),
("broke", "a fait d<e'>border"),("without", "sans"),("The!straw", "La goutte d'eau"),
("camel's!back.", "vase.")]


data Entry = Entry {wrd, def :: String, len :: Int, phr :: Bool}
        deriving Show

-- English-to-French, hash-table section

entries :: [Entry]
entries = map (\(x, y) -> Entry x y (length x) (' ' `elem` x)) icards

type Run = [Entry]

maxl = maximum [len e | e <- entries]

runs :: [Run]
runs = f 0 $ groupBy ((==) `on` len) $ sortBy (compare `on` len) entries
  where f _ [] = []
        f i (r @ (Entry {len = l} : _) : rs) | i == l = r  : f (i + 1) rs
        f i                              rs           = [] : f (i + 1) rs

type Word = String

search' :: Word -> [Entry] -> String
search' searchWord subList 
search' _ [] = "unknown"
search' ([def x | x <- subList,  (wrd x) == searchWord])==[] = "no match"
search' = [def x | x <- subList,  (wrd x) == searchWord]

--search' searchWord subList = (def | x <- subList,  (wrd x) == searchWord)
--search' searchWord subList = [def x::String | x <- subList,  (wrd x) == searchWord]
--search' searchWord [subList] = [def | x <- subList,  (wrd x) == searchWord]
--search' searchWord subList = [def | x <- subList,  (wrd x) == searchWord]
--search' searchWord subList = [def x | x <- subList,  (wrd x) == searchWord]
--search' searchWord subList = [x->def | x <- subList,  (x->wrd) == searchWord]

search :: [Run] -> Word -> String
search runList searchWord = search' searchWord $ runList!!wrdLen
                     where wrdLen = (length searchWord)

I need help with the search' function. GHCi will tell me that expected type is char... and actual type is Entry-> String.

But I expected type to be the string. I don't know why it thinks I want just a char.

In general here is what I expect: Send a [Run] and a Word to Search, where [Run] = [[Entries]] and Word = String

the [Run] should be formatted so all of the Entries in [Run]!!0 are length 0, [Run]!!1 are length 1 etc.

So, function search should check the length of the sent Word, then call search' and send it the subList associated to the list of entries that have the same length as the word.

Once inside search' I just want to do a linear search of the list for the wrd == Word , then return the def of that word.

any help would be fantastic.

There are two separate problems:

1. You should apply def to an Entry if you want a String . So, the definition of search' should look like this:

search' searchWord subList = [def x | x <- subList, wrd x == searchWord]

2. It is not obvious, a priori, that searching will always find exactly one match. There may be no matches, or many matches. (I understand that you may expect that the data you provide will result in exactly one match, but that kind of reasoning is a bit beyond what can be done both efficiently and statically.) So, your search' and search functions should return lists. The type signatures should look like this:

search' :: Word -> [Entry] -> [String]
search :: [Run] -> Word -> [String]

...and, indeed, if you leave the type signatures off, GHC will infer exactly those types (up to type synonyms).

edit: To address the updated question, you probably want something like this:

search' searchWord subList = case [def x | x <- subList, wrd x == searchWord] of
    [] -> "no match"
    (match:_) -> match

Learn You a Haskell has a section about pattern matching if you want to know more. It also has a section on lists and list comprehensions , and is generally just a good tutorial.

However, I strongly advise against writing search' this way: it's a bit dishonest! (For example, as the caller of search' , how can I differentiate between the result 'the search succeeded, and the translation is "no match"' and the result 'the search failed'?)

Hm, let's see. You have a list of stuff, [a] . You have some criteria for determining whether or not the search succeeded, a -> Bool . And you want to perform the search on the list, returning a value of the element type a . Stop...Hoogle time! Hoogling [a] -> (a -> Bool) -> a , the top hit is find :: (a -> Bool) -> [a] -> Maybe a . The only catch is that it returns a Maybe a : it will either find Just something or Nothing . I'd say this is an appropriate upgrade for your search function.

search :: [Run] -> Word -> Maybe Entry
search runList searchWord = find (\x -> wrd x == searchWord) $ runList!!wrdLen
  where wrdLen = (length searchWord)

Since we've changed the contract for search to produce a Maybe Entry instead of a simple String , if you were using it like this before:

doStuff (search runList searchWord)

You will now have to take into account the possibility of the search failing.

case search runList searchWord of
  Just foundWord -> doStuff (def foundWord)
  Nothing        -> doSomethingElse

If you are absolutely sure that the search will never fail, you can unwrap it with fromJust

doStuff (fromJust $ def $ search runList searchWord)

Although fromJust is generally discouraged.


Now, one other thing. You said you wanted to return only the def , not the entire Entry . As you should know, we can use def :: Entry -> String as a field accessor to extract the def out of an Entry . But how do we apply this to a Maybe Entry ?

Stop...Hoogle time! We have a value, v :: Maybe a . We have a function that works on plain old a values, f :: a -> b . We want to somehow apply f to v , yielding a result of type b . Hoogling Maybe a -> (a -> b) -> b , I see two good options.

maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing = n
maybe _ f (Just x) = f x

The maybe function takes a function and a maybe value, and also a default . If the maybe value turns out to be Nothing , it just uses the default. Otherwise, it uses the function f on the value inside of the Just constructor.

search :: [Run] -> Word -> String
search runList searchWord = search' (\x -> wrd x == searchWord) $ runList!!wrdLen
  where wrdLen = (length searchWord)

search' :: (Entry -> Bool) -> [Entry] -> String
search' f es = maybe "not found" def $ find f es
-- or eta reduce: search' = maybe "not found" def . find

This solution is OK, but I prefer the next one better.

fmap :: Functor f => (a -> b) -> f a -> f b

If you are not familiar with functors, I highly recommend Learn you a Haskell > the Functor typeclass . Maybe is a functor, which means we can use fmap on maybe values.

search' :: (Entry -> Bool) -> [Entry] -> Maybe String
search' f es = fmap def $ find f es

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM