Haskell – 146 – elementi base del linguaggio – 9

Continuo da qui, copio qui.

Interattività
If you are familiar with books on other (imperative) languages, you might be wondering why you haven’t seen many of the standard programs written in tutorials of other languages (like ones that ask the user for his name and then says “Hi” to him by name). The reason for this is simple: Being a pure functional language, it is not entirely clear how one should handle operations like user input.

After all, suppose you have a function that reads a string from the keyboard. If you call this function twice, and the user types something the first time and something else the second time, then you no longer have a function, since it would return two different values. The solution to this was found in the depths of category theory, a branch of formal mathematics: monads. We’re not yet ready to talk about monads formally, but for now, think of them simply as a convenient way to express operations like input/output. We’ll discuss them in this context much more in the chapter Io and then discuss monads for monads’ sake in the chapter Monads [prossimamente].

Suppose we want to write a function that’s interactive. The way to do this is to use the do keyword. This allows us to specify the order of operations (remember that normally, since Haskell is a lazy language, the order in which operations are evaluated in it is unspecified). So, to write a simple program that asks a user for his name and then address him directly, enter the following code into “Name.hs“:

Names.hs

module Main
  where

import System.IO

main = do
  hSetBuffering stdin LineBuffering
  putStrLn "Please enter your name: "
  name <- getLine
  putStrLn ("Hello, " ++ name ++ ", how are you?")

Note: The parentheses are required on the second instance of putStrLn but not the first. This is because function application binds more tightly than ++, so without the parentheses, the second would be interpreted as (putStrLn "Hello, ") ++ name ++ ....

You can then either load this code in your interpreter and execute main by simply typing “main,” or you can compile it and run it from the command line. I’ll show the results of the interactive approach:

Prelude> :l Names
[1 of 1] Compiling Main             ( Names.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Please enter your name:
Juhan
Hello, Juhan, how are you?

And there’s interactivity. Let’s go back and look at the code a little, though. We name the module “Main,” so that we can compile it. We name the primary function “main,” so that the compiler knows that this is the function to run when the program is run. On the fourth line, we import the IO library, so that we can access the IO functions. On the seventh line, we start with do, telling Haskell that we’re executing a sequence of commands.

The first command is hSetBuffering stdin LineBuffering, which you should probably ignore for now (incidentally, this is only required by GHC — in Hugs you can get by without it). The necessity for this is because, when GHC reads input, it expects to read it in rather large blocks. A typical person’s name is nowhere near large enough to fill this block. Thus, when we try to read from stdin, it waits until it’s gotten a whole block. We want to get rid of this, so we tell it to use LineBuffering instead of block buffering.

The next command is putStrLn, which prints a string to the screen. On the ninth line, we say name <- getLine. This would normally be written name = getLine, but using the arrow instead of the equal sign shows that getLine isn’t a real function and can return different values. This command means “run the action getLine, and store the results in name.”

The last line constructs a string using what we read in the previous line and then prints it to the screen.

Another example of a function that isn’t really a function would be one that returns a random value. In this context, a function that does this is called randomRIO. Using this, we can write a “guess the number” program. Enter the following code into “Guess.hs“:

Guess.hs

module Main
  where

import System.IO
import System.Random

main = do
  hSetBuffering stdin LineBuffering
  num <- randomRIO (1::Int, 100)
  putStrLn "I'm thinking of a number between 1 and 100"
  doGuessing num

doGuessing num = do
  putStrLn "Enter your guess:"
  guess <- getLine
  let guessNum = read guess
  if guessNum < num 
    then do putStrLn "Too low!" 
            doGuessing num 
    else if guessNum > num
           then do putStrLn "Too high!"
                   doGuessing num
           else do putStrLn "You Win!"

Let’s examine this code. On the fifth line we write “import Random” to tell the compiler that we’re going to be using some random functions (these aren’t built into the Prelude). In the first line of main, we ask for a random number in the range (1,100). We need to write ::Int to tell the compiler that we’re using integers here — not floating point numbers or other numbers. We’ll talk more about this in the section on Type basics [prossimamente]. On the next line, we tell the user what’s going on, and then, on the last line of main, we tell the compiler to execute the command doGuessing.

The doGuessing function takes the number the user is trying to guess as an argument. First, it asks the user to guess and then accepts their guess (which is a String) from the keyboard. The if statement checks first to see if their guess is too low. However, since guess is a string, and num is an integer, we first need to convert guess to an integer by reading it. Since “read guess” is a plain, pure function (and not an IO action), we don’t need to use the <- notation (in fact, we cannot); we simply bind the value to guessNum. Note that while we’re in do notation, we don’t need ins for lets.

If they guessed too low, we inform them and then start doGuessing over again. If they didn’t guess too low, we check to see if they guessed too high. If they did, we tell them and start doGuessing again. Otherwise, they didn’t guess too low and they didn’t guess too high, so they must have gotten it correct. We tell them that they won and exit. The fact that we exit is implicit in the fact that there are no commands following this. We don’t need an explicit return() statement.

You can either compile this code or load it into your interpreter, and you will get something like:

Prelude> :l Guess
[1 of 1] Compiling Main             ( Guess.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
I'm thinking of a number between 1 and 100
Enter your guess:
50
Too high!
Enter your guess:
25
Too high!
Enter your guess:
12
Too high!
Enter your guess:
6
Too high!
Enter your guess:
3
Too low!
Enter your guess:
4
Too low!
Enter your guess:
5
You Win!

The recursive action that we just saw doesn’t actually return a value that we use in any way. In the case when it does, the “obvious” way to write the command is actually incorrect. Here, we will give the incorrect version, explain why it is wrong, then give the correct version.

Let’s say we’re writing a simple program that repeatedly asks the user to type in a few words. If at any point the user enters the empty word (i.e., he just hits enter without typing anything), the program prints out everything he’s typed up until that point and then exits. The primary function (actually, an action) in this program is one that asks the user for a word, checks to see if it’s empty, and then either continues or ends. The incorrect formulation of this might look something like:

askForWords = do
  putStrLn "Please enter a word:"
  word <- getLine
  if word == ""
    then return []
    else return (word : askForWords)

Before reading ahead, see if you can figure out what is wrong with the above code.

The error is on the last line, specifically with the term word : askForWords. Remember that when using (:), we are making a list out of an element (in this case word) and another list (in this case, askForWords). However, askForWords is not a list; it is an action that, when run, will produce a list. That means that before we can attach anything to the front, we need to run the action and take the result. In this case, we want to do something like:

askForWords = do
  putStrLn "Please enter a word:"
  word <- getLine
  if word == ""
    then return []
    else do
      rest <- askForWords
      return (word : rest)

Here, we first run askForWords, take the result and store it in the variable rest. Then, we return the list created from word and rest.

By now, you should have a good understanding of how to write simple functions, compile them, test functions and programs in the interactive environment, and manipulate lists.

Pausa 😁 Poi eseguirò Guess compilandolo (in 2 modi) e infine l’esercizio che Hal propone 😁

👽

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: