How do I add x tuples into a list x number of times?

后端 未结 5 1505
挽巷
挽巷 2021-01-25 20:59

I have a question about tuples and lists in Haskell. I know how to add input into a tuple a specific number of times. Now I want to add tuples into a list an unknown number of t

相关标签:
5条回答
  • 2021-01-25 21:18

    When doing functional programming, it is often better to think about composition of operations instead of individual steps. So instead of thinking about it like adding tuples one at a time to a list, we can approach it by first dividing the input into a list of strings, and then converting each string into a tuple.

    Assuming the tuples are written each on one line, we can split the input using lines, and then use read to parse each tuple. To make it work on the entire list, we use map.

    main = do input <- getContents
              let tuples = map read (lines input) :: [(String, Integer)]
              print tuples
    

    Let's try it.

    $ runghc Tuples.hs
    ("Hello", 2)
    ("Haskell", 4)
    

    Here, I press Ctrl+D to send EOF to the program, (or Ctrl+Z on Windows) and it prints the result.

    [("Hello",2),("Haskell",4)]
    

    If you want something more interactive, you will probably have to do your own recursion. See Daniel Wagner's answer for an example of that.

    0 讨论(0)
  • 2021-01-25 21:22

    Sorry, you can't1. There are fundamental differences between tuples and lists:

    • A tuple always have a finite amount of elements, that is known at compile time. Tuples with different amounts of elements are actually different types.
    • List an have as many elements as they want. The amount of elements in a list doesn't need to be known at compile time.
    • A tuple can have elements of arbitrary types. Since the way you can use tuples always ensures that there is no type mismatch, this is safe.
    • On the other hand, all elements of a list have to have the same type. Haskell is a statically-typed language; that basically means that all types are known at compile time.

    Because of these reasons, you can't. If it's not known, how many elements will fit into the tuple, you can't give it a type.

    I guess that the input you get from your user is actually a string like "(1,2,3)". Try to make this directly a list, whithout making it a tuple before. You can use pattern matching for this, but here is a slightly sneaky approach. I just remove the opening and closing paranthesis from the string and replace them with brackets -- and voila it becomes a list.

    tuplishToList :: String -> [Int]
    tuplishToList str = read ('[' : tail (init str) ++ "]")
    

    Edit

    Sorry, I did not see your latest comment. What you try to do is not that difficult. I use these simple functions for my task:

    • words str splits str into a list of words that where separated by whitespace before. The output is a list of Strings. Caution: This only works if the string inside your tuple contains no whitespace. Implementing a better solution is left as an excercise to the reader.

    • map f lst applies f to each element of lst

    • read is a magic function that makes a a data type from a String. It only works if you know before, what the output is supposed to be. If you really want to understand how that works, consider implementing read for your specific usecase.

    And here you go:

    tuplish2List :: String -> [(String,Int)]
    tuplish2List str = map read (words str)
    

    1 As some others may point out, it may be possible using templates and other hacks, but I don't consider that a real solution.

    0 讨论(0)
  • 2021-01-25 21:28

    I'm guessing that, since this is for a class, and you've been studying Haskell for < 1 week, you don't actually need to do any input/output. That's a bit more advanced than you probably are, yet. So:

    • As others have said, map fst will take a list of tuples, of arbitrary length, and return the first elements. You say you know how to do that. Fine.

    • But how do the tuples get into the list in the first place? Well, if you have a list of tuples and want to add another, (:) does the trick. Like so:

      oldList = [("first", 1), ("second", 2)]
      newList = ("third", 2) : oldList
      

      You can do that as many times as you like. And if you don't have a list of tuples yet, your list is [].

    Does that do everything that you need? If not, what specifically is it missing?

    Edit: With the corrected type:

    Eq a => [(a, b)]

    That's not the type of a function. It's the type of a list of tuples. Just have the user type yourFunctionName followed by [ ("String1", val1), ("String2", val2), ... ("LastString", lastVal)] at the prompt.

    0 讨论(0)
  • 2021-01-25 21:30

    There's a lot of things you could possibly mean. For example, if you want a few copies of a single value, you can use replicate, defined in the Prelude:

    replicate :: Int -> a -> [a]
    replicate 0 x = []
    replicate n | n < 0     = undefined
                | otherwise = x : replicate (n-1) x
    

    In ghci:

    Prelude> replicate 4 ("Haskell", 2)
    [("Haskell",2),("Haskell",2),("Haskell",2),("Haskell",2)]
    

    Alternately, perhaps you actually want to do some IO to determine the list. Then a simple loop will do:

    getListFromUser = do
        putStrLn "keep going?"
        s <- getLine
        case s of
            'y':_ -> do
                putStrLn "enter a value"
                v <- readLn
                vs <- getListFromUser
                return (v:vs)
            _ -> return []
    

    In ghci:

    *Main> getListFromUser :: IO [(String, Int)]
    keep going?
    y
    enter a value
    ("Haskell",2)
    keep going?
    y
    enter a value
    ("Prolog",4)
    keep going?
    n
    [("Haskell",2),("Prolog",4)]
    

    Of course, this is a particularly crappy user interface -- I'm sure you can come up with a dozen ways to improve it! But the pattern, at least, should shine through: you can use values like [] and functions like : to construct lists. There are many, many other higher-level functions for constructing and manipulating lists, as well.

    P.S. There's nothing particularly special about lists of tuples (as compared to lists of other things); the above functions display that by never mentioning them. =)

    0 讨论(0)
  • 2021-01-25 21:30

    One simple solution to this would be to use a list comprehension, as so (done in GHCi):

    Prelude> let fstMap tuplist = [fst x | x <- tuplist]
    Prelude> fstMap [("String1",1),("String2",2),("String3",3)]
    ["String1","String2","String3"]
    Prelude> :t fstMap
    fstMap :: [(t, b)] -> [t]
    

    This will work for an arbitrary number of tuples - as many as the user wants to use.

    To use this in your code, you would just write:

    fstMap :: Eq a => [(a,b)] -> [a]
    fstMap tuplist = [fst x | x <- tuplist]
    

    The example I gave is just one possible solution. As the name implies, of course, you can just write:

    fstMap' :: Eq a => [(a,b)] -> [a]
    fstMap' = map fst
    

    This is an even simpler solution.

    0 讨论(0)
提交回复
热议问题