Tải bản đầy đủ (.pdf) (13 trang)

"HASKELL CHEAT SHEET"

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (156.31 KB, 13 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

<b>Haskell Cheat Sheet</b>

This cheat sheet lays out the fundamental elementsof the Haskell language: syntax, keywords andother elements. It is presented as both an ex-ecutable Haskell file and a printable document.Load the source into your favorite interpreter toplay with code samples shown.

Comments above function definitions shouldstart with ‘{- |’ and those next to parameter typeswith ‘-- ^’ for compatibility with Haddock, a sys-tem for documenting Haskell code.

<b>Reserved Words</b>

The following lists the reserved words defined byHaskell. It is a syntax error to give a variable orfunction one of these names.

case, class, data, deriving, do,else, if, import, in, infix, infixl,infixr, instance, let, of, module,newtype, then, type, where

string1 = "My longstring."

However, backslashes (‘\’) can be used to “escape”around the new line:

string1 = "My long \\string."

The area between the backslashes is ignored. Animportant note is that new lines in the string muststill be represented explicitly:

string2 = "My long \n\\string."

That is,string1 evaluates to:My long string.

Whilestring2 evaluates to:My long

In fact, any value which is in theEnum class can beused. E.g.,:

['a' .. 'z'] – List of characters – a, b, . . ., z.[1.0, 1.5 .. 2] – [1.0,1.5,2.0].

[UppercaseLetter ..] – List of GeneralCategoryvalues (fromData.Char).

<b>Lists & Tuples</b>

[] – Empty list.

[1,2,3] – List of three numbers.

1 : 2 : 3 : [] – Alternate way to write lists ing “cons” (:) and “nil” ([]).

us-"abc" – List of three characters (strings are lists).'a' : 'b' : 'c' : [] – List of characters (sameas"abc").

(1,"a") – 2-element tuple of a number and a string.(head, tail, 3, 'a') – 4-element tuple of twofunctions, a number and a character.

<b>“Layout” rule, braces and semi-colons.</b>

Haskell can be written using braces and colons, just like C. However, no one does. Instead,the “layout” rule is used, where spaces represent

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

semi-scope. The general rule is – always indent. Whenthe compiler complains, indent more.

<b>Braces and semi-colons</b> Semi-colons terminatean expression, and braces represent scope. Theycan be used after several keywords: where, let, doandof. They cannot be used when defining a func-tion body. For example, the below will not compile.

square2 x = { x * x; }However, this will work fine:

Unless a where clause is present. In that case, dent the where clause at least one space from thefunction name and any function bodies at least onespace from thewhere keyword:

in-square x =x2

where x2 =x * x

<b>Let</b> Indent the body of the let at least one spacefrom the first definition in thelet. If let appearson its own line, the body of any definition mustappear in the column after the let:

square x =let x2 =

x * xin x2

As can be seen above, thein keyword must also bein the same column aslet. Finally, when multipledefinitions are given, all identifiers must appear inthe same column.

data Choices = First String | Second |Third | Fourth

case can be used to determine which choice wasgiven:

whichChoice ch =case ch of

First _ -> "1st!"Second -> "2nd!"

_ -> "Something else."

As with pattern-matching in function definitions,the ‘_’ character is a “wildcard” and matches anyvalue.

<b>Nesting & Capture</b> Nested matching and ment capture are also allowed. Referring to thedefinition ofMaybe below, we can determine if anychoice was given using a nested match:

argu-anyChoice1 ch =

case ch of

Nothing -> "No choice!"Just (First _) -> "First!"Just Second -> "Second!"_ -> "Something else."

We can use argument capture to display the valuematched if we wish:

anyChoice2 ch =case ch of

Nothing -> "No choice!"Just score@(First "gold") ->

"First with gold!"Just score@(First _) ->

"First with something else: "++ show score

_ -> "Not first."

<b>Matching Order</b> Matching proceeds from top tobottom. If we re-wroteanyChoice1 as below, we’llnever know what choice was actually given becausethe first pattern will always succeed:

anyChoice3 ch =case ch of

_ -> "Something else."Nothing -> "No choice!"Just (First _) -> "First!"Just Second -> "Second!"

<b>Guards</b> Guards, or conditional matches, can beused in cases just like function definitions. The onlydifference is the use of the -> instead of =. Hereis a simple function which does a case-insensitivestring match:

strcmp [] [] = True

strcmp s1 s2 = case (s1, s2) of

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

(s1:ss1, s2:ss2)

| toUpper s1 == toUpper s2 ->strcmp ss1 ss2

| otherwise -> False_ -> False

A Haskell function is defined to work on a certaintype or set of types and cannot be defined morethan once. Most languages support the idea of“overloading”, where a function can have differentbehavior depending on the type of its arguments.Haskell accomplishes overloading through classand instance declarations. A class defines oneor more functions that can be applied to any typeswhich are members (i.e., instances) of that class. Aclass is analogous to an interface in Java or C, andinstances to a concrete implementation of the inter-face.

A class must be declared with one or more typevariables. Technically, Haskell 98 only allows onetype variable, but most implementations of Haskellsupport so-called multi-parameter type classes, whichallow more than one type variable.

We can define a class which supplies a flavor fora given type:

class Flavor a whereflavor :: a -> String

Notice that the declaration only gives the type nature of the function - no implementation is givenhere (with some exceptions, see “Defaults” below).Continuing, we can define several instances:

sig-instance Flavor Bool whereflavor _ = "sweet"

instance Flavor Char whereflavor _ = "sour"

Evaluatingflavor True gives:> flavor True

func-class Eq a where

(==) :: a -> a -> Bool(/=) :: a -> a -> Bool(/=) a b = not (a == b)

In fact, recursive definitions can be created, but oneclass member must always be implemented by anyinstance declarations.

So-called algebraic data types can be declared as lows:

fol-data MyType = MyValue1 | MyValue2

MyType is the type’s name. MyValue1 andMyValue are values of the type and are called con-structors. Multiple constructors are separated with

the ‘|’ character. Note that type and constructornames must start with a capital letter. It is a syntaxerror otherwise.

<b>Constructors with Arguments</b> The type aboveis not very interesting except as an enumeration.Constructors that take arguments can be declared,allowing more information to be stored with yourtype:

data Point = TwoD Int Int| ThreeD Int Int Int

Notice that the arguments for each constructor aretype names, not constructors. That means this kindof declaration is illegal:

data Poly = Triangle TwoD TwoD TwoDinstead, thePoint type must be used:

data Poly = Triangle Point Point Point

<b>Type and Constructor Names</b> Type and structor names can be the same, because they willnever be used in a place that would cause confu-sion. For example:

con-data User = User String | Admin Stringwhich declares a type named User with two con-structors, User and Admin. Using this type in afunction makes the difference clear:

whatUser (User _) = "normal user."whatUser (Admin _) = "admin user."

Some literature refers to this practice as type ning.

<b>pun-Type Variables</b> Declaring so-called polymorphicdata types is as easy as adding type variables in thedeclaration:

data Slot1 a = Slot1 a | Empty1

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

This declares a type Slot1 with two constructors,Slot1 and Empty1. The Slot1 constructor can takean argument of any type, which is represented bythe type variablea above.

We can also mix type variables and specifictypes in constructors:

data Slot2 a = Slot2 a Int | Empty2

Above, the Slot2 constructor can take a value ofany type and anInt value.

<b>Record Syntax</b> Constructor arguments can bedeclared either positionally, as above, or usingrecord syntax, which gives a name to each argu-ment. For example, here we declare aContact typewith names for appropriate arguments:

data Contact = Contact { ctName :: String, ctEmail :: String

, ctPhone :: String }

These names are referred to as selector or accessorfunctions and are just that, functions. They muststart with a lowercase letter or underscore and can-not have the same name as another function inscope. Thus the “ct” prefix on each above. Mul-tiple constructors (of the same type) can use thesame accessor function for values of the same type,but that can be dangerous if the accessor is not usedby all constructors. Consider this rather contrivedexample:

data Con = Con { conValue :: String }| Uncon { conValue :: String }| Noncon

whichCon con = "convalue is " ++conValue con

IfwhichCon is called with a Noncon value, a runtimeerror will occur.

Finally, as explained elsewhere, these namescan be used for pattern matching, argument cap-ture and “updating.”

<b>Class Constraints</b> Data types can be declaredwith class constraints on the type variables, butthis practice is generally discouraged. It is gener-ally better to hide the “raw” data constructors us-ing the module system and instead export “smart”constructors which apply appropriate constraints.In any case, the syntax used is:

data (Num a) => SomeNumber a = Two a a| Three a a a

This declares a type SomeNumber which has onetype variable argument. Valid types are those intheNum class.

<b>Deriving</b> Many types have common operationswhich are tedious to define yet very necessary, suchas the ability to convert to and from strings, com-pare for equality, or order in a sequence. Thesecapabilities are defined as typeclasses in Haskell.

Because seven of these operations are so mon, Haskell provides the deriving keywordwhich will automatically implement the typeclasson the associated type. The seven supported type-classes are: Eq, Read, Show, Ord, Enum, Ix, andBounded.

com-Two forms ofderiving are possible. The first isused when a type only derives on class:

data Priority = Low | Medium | Highderiving Show

The second is used when multiple classes are rived:

de-data Alarm = Soft | Loud | Deafeningderiving (Read, Show)

It is a syntax error to specifyderiving for any otherclasses besides the six given above.

<b>If and IO</b> if is tricky when used with IO.Conceptually it is are no different, but intuitivelyit is hard to deal with. Consider the functiondoesFileExists from System.Directory:

doesFileExist :: FilePath -> IO BoolTheif statement has this “signature”:

if-then-else :: Bool -> a -> a -> aThat is, it takes aBool value and evaluates to someother value based on the condition. From the typesignatures it is clear thatdoesFileExist cannot beused directly byif:

wrong fileName =

if doesFileExist fileNamethen ...

else ...

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

That is,doesFileExist results in an IO Bool value,while if wants a Bool value. Instead, the correctvalue must be “extracted” by running the IO ac-tion:

right1 fileName = do

exists <- doesFileExist fileNameif exists

then return 1else return 0

Notice the use of return, too. Because do puts us“inside” the IO monad, we can’t “get out” exceptthrough return. Note that we don’t have to useif inline here - we can also use let to evaluate thecondition and get a value first:

right2 fileName = do

exists <- doesFileExist fileNamelet result =

if existsthen 1else 0return result

Again, notice where return is. We don’t put it inthelet statement. Instead we use it once at the endof the function.

<b>Multipledo’s</b> When using do with if or case,anotherdo is required if either branch has multiplestatements. An example withif:

countBytes1 f =do

putStrLn "Enter a filename."args <- getLine

if length args == 0-- no 'do'.

then putStrLn "No filename given."

countBytes2 =do

putStrLn "Enter a filename."args <- getLine

countBytes3 =do

putStrLn "Enter a filename."args <- getLine

See the section onmodule below.

<b>If, Then, Else</b>

Remember, if always “returns” a value. It is anexpression, not just a control flow statement. Thisfunction tests if the string given starts with a lowercase letter and, if so, converts it to upper case:

-- Use pattern-matching to-- get first charactersentenceCase (s:rest) =

<b>Infix, infixl and infixr</b>

See the section on operators below.

See the section onclass above.

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

Local functions can be defined within a function inglet. let is always followed by in. in must ap-pear in the same column as thelet keyword. Func-tions defined have access to all other functions andvariables within the same scope (including thosedefined by let). In this example, mult multipliesits argumentn by x, which was passed to the orig-inal multiples. mult is used by map to give themultiples of x up to 10:

us-multiples x =

let mult n = n * xin map mult [1..10]

let “functions” with no arguments are actuallyconstants and, once evaluated, will not evaluateagain. This is useful for capturing common por-tions of your function and re-using them. Here is asilly example which gives the sum of a list of num-bers, their average, and their median:

listStats m =

let numbers = [1,3 .. m]total = sum numbers

mid = head (take (m `div` 2)numbers)in "total: " ++ show total ++

", mid: " ++ show mid

<b>Deconstruction</b> The left-hand side of alet inition can also deconstruct its argument, in casesub-components are going to be accessed. This def-inition would extract the first three characters froma string

def-firstThree str =let (a:b:c:_) = str

in "Initial three characters are: " ++show a ++ ", " ++

show b ++ ", and " ++show c

Note that this is different than the following, whichonly works if the string has three characters:

onlyThree str =let (a:b:c) = str

in "The characters given are: " ++show a ++ ", " ++ show b ++", and " ++ show c

func-module MyModule where

Module names must start with a capital letter butotherwise can include periods, numbers and un-derscores. Periods are used to give sense of struc-ture, and Haskell compilers will use them as indi-cations of the directory a particular source file is,but otherwise they have no meaning.

The Haskell community has standardized a setof top-level module names such as Data, System,Network, etc. Be sure to consult them for an appro-priate place for your own module if you plan onreleasing it to the public.

<b>Imports</b> The Haskell standard libraries are vided into a number of modules. The functionalityprovided by those libraries is accessed by import-ing into your source file. To import all everythingexported by a library, just use the module name:

di-import Text.Read

Everything means everything: functions, data typesand constructors, class declarations, and even othermodules imported and then exported by the thatmodule. Importing selectively is accomplished bygiving a list of names to import. For example, herewe import some functions fromText.Read:

import Text.Read (readParen, lex)

Data types can imported in a number of ways. Wecan just import the type and no constructors:

import Text.Read (Lexeme)

Of course, this prevents our module from matching on the values of type Lexeme. We canimport one or more constructors explicitly:

pattern-import Text.Read (Lexeme(Ident, Symbol))All constructors for a given type can also be im-ported:

import Text.Read (Lexeme(..))

We can also import types and classes defined in themodule:

import Text.Read (Read, ReadS)

In the case of classes, we can import the functionsdefined for the using syntax similar to importingconstructors for data types:

import Text.Read (Read(readsPrec, readList))

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

Note that, unlike data types, all class functions areimported unless explicitly excluded. To only importthe class, we use this syntax:

import Text.Read (Read())

<b>Exclusions</b> If most, but not all, names are goingto imported from a module, it would be tedious tospecify all those names except a few. For that rea-son, imports can also be specified via the hidingkeyword:

import Data.Char hiding (isControl, isMark)

Except for instance declarations, any type, function,constructor or class can be hidden.

<b>Instance Declarations</b> It must be noted thatinstance declarations cannot be excluded from im-port. Any instance declarations in a module willbe imported when the module is imported.

<b>Qualified Imports</b> The names exported by amodule (i.e., functions, types, operators, etc.) canhave a prefix attached through qualified imports.This is particularly useful for modules which havea large number of functions having the same nameasPrelude functions. Data.Set is a good example:

import qualified Data.Set as Set

This form requires any function, type, constructoror other name exported byData.Set to now be pre-fixed with the alias (i.e.,Set) given. Here is one wayto remove all duplicates from a list:

removeDups a =

Set.toList (Set.fromList a)

A second form does not create an alias. Instead,the prefix becomes the module name. We can writea simple function to check if a string is all uppercase:

import qualified CharallUpper str =

all Char.isUpper str

Except for the prefix specified, qualified importssupport the same syntax as normal imports. Thename imported can be limited in the same ways asdescribed above.

<b>Exports</b> If an export list is not provided, then allfunctions, types, constructors, etc. will be availableto anyone importing the module. Note that any im-ported modules are not exported in this case. Limit-ing the names exported is accomplished by addinga parenthesized list of names before thewhere key-word:

module MyModule (MyType, MyClass

, myFunc1...)where

The same syntax as used for importing can be usedhere to specify which functions, types, construc-tors, and classes are exported, with a few differ-ences. If a module imports another module, it canalso export that module:

module MyBigModule (module Data.Set, module Data.Char)

import Data.Setimport Data.Char

A module can even re-export itself, which can beuseful when all local definitions and a given im-ported module are to be exported. Below we exportourselves andData.Set, but not Data.Char:

module AnotherBigModule (module Data.Set, module AnotherBigModule)

import Data.Setimport Data.Char

While data introduces new values and type justcreates synonyms, newtype falls somewhere be-tween. The syntax fornewtype is quite restricted –only one constructor can be defined, and that con-structor can only take one argument. Continuingthe example above, we can define aPhone type likethe following:

newtype Home = H Stringnewtype Work = W Stringdata Phone = Phone Home Work

As opposed to type, the H and W “values” onPhone are not just String values. The typecheckertreats them as entirely new types. That means ourlowerName function from above would not compile.The following produces a type error:

lPhone (Phone hm wk) =

Phone (lower hm) (lower wk)

Instead, we must use pattern-matching to get to the“values” to which we applylower:

lPhone (Phone (H hm) (W wk)) =

Phone (H (lower hm)) (W (lower wk))

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

The key observation is that this keyword does notintroduce a new value; instead it introduces a newtype. This gives us two very useful properties:

• No runtime cost is associated with the newtype, since it does not actually produce newvalues. In other words, newtypes are abso-lutely free!

• The type-checker is able to enforce that mon types such asInt or String are used inrestricted ways, specified by the programmer.Finally, it should be noted that any derivingclause which can be attached to adata declarationcan also be used when declaring anewtype.

data Person = Person String String

where the first constructor argument representstheir first name and the second their last. How-ever, the order and meaning of the two argumentsis not very clear. Atype declaration can help:

type FirstName = Stringtype LastName = String

data Person = Person FirstName LastName

Becausetype introduces a synonym, type checkingis not affected in any way. The functionlower, de-fined as:

lower s = map toLower swhich has the type

lower :: String -> String

can be used on values with the type FirstName orLastName just as easily:

lName (Person f l ) =

Person (lower f) (lower l)

Because type is just a synonym, it can’t declaremultiple constructors likedata can. Type variablescan be used, but there cannot be more than thetype variables declared with the original type. Thatmeans a synonym like the following is possible:

type NotSure a = Maybe abut this not:

type NotSure a b = Maybe a

Note that fewer type variables can be used, whichuseful in certain instances.

Similar to let, where defines local functions andconstants. The scope of a where definition is thecurrent function. If a function is broken into multi-ple definitions through pattern-matching, then thescope of a particular where clause only applies tothat definition. For example, the function resultbelow has a different meaning depending on thearguments given to the functionstrlen:

strlen [] = result

where result = "No string given!"strlen f = result ++ " characters long!"

where result = show (length f)

<b>Where vs. Let</b> Awhere clause can only be fined at the level of a function definition. Usually,that is identical to the scope oflet definition. Theonly difference is when guards are being used. Thescope of thewhere clause extends over all guards.In contrast, the scope of a let expression is onlythe current function clause and guard, if any.

<b>de-Declarations, Etc.</b>

The following section details rules on function larations, list comprehensions, and other areas ofthe language.

func--- Matches when the string "y" is given.agree1 "y" = "Great!"

-- Matches when the string "n" is given.agree1 "n" = "Too bad."

-- Matches when string beginning

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

-- with 'y' given.

agree1 ('y':_) = "YAHOO!"

-- Matches for any other value given.agree1 _ = "SO SAD."

Note that the ‘_’ character is a wildcard andmatches any value.

Pattern matching can extend to nested values.Assuming this data declaration:

data Bar = Bil (Maybe Int) | Bazand recallingMaybe is defined as:

data Maybe a = Just a | Nothing

we can match on nestedMaybe values when Bil ispresent:

f (Bil (Just _)) = ...f (Bil Nothing) = ...f Baz = ...

Pattern-matching also allows values to be assignedto variables. For example, this function determinesif the string given is empty or not. If not, the valuecaptures instr is converted to to lower case:

toLowerStr [] = []

toLowerStr str = map toLower str

In reality,str is the same as _ in that it will matchanything, except the value matched is also given aname.

n+ <b>k Patterns</b> This sometimes controversialpattern-matching facility makes it easy to matchcertain kinds of numeric expressions. The ideais to define a base case (the “n” portion) with aconstant number for matching, and then to defineother matches (the “k” portion) as additives to thebase case. Here is a rather inefficient way of testingif a number is even or not:

isEven 0 = TrueisEven 1 = False

isEven (n + 2) = isEven n

<b>Argument Capture</b> Argument capture is usefulfor pattern-matching a value AND using it, with-out declaring an extra variable. Use an @ symbolin between the pattern to match and the variable toassign the value to. This facility is used below tocapture the head of the list in l for display, whilealso capturing the entire list inls in order to com-pute its length:

len ls@(l:_) = "List starts with " ++show l ++ " and is " ++

show (length ls) ++ " items long."len [] = "List is empty!"

<b>Guards</b> Boolean functions can be used as“guards” in function definitions along with patternmatching. An example without pattern matching:

which n

| n == 0 = "zero!"| even n = "even!"| otherwise = "odd!"

Noticeotherwise – it always evaluates to true andcan be used to specify a “default” branch.

Guards can be used with patterns. Here is afunction that determines if the first character in astring is upper or lower case:

what [] = "empty string!"what (c:_)

| isUpper c = "upper case!"| isLower c = "lower case"| otherwise = "not a letter!"

<b>Matching & Guard Order</b> Pattern-matchingproceeds in top to bottom order. Similarly, guardexpressions are tested from top to bottom. For ex-ample, neither of these functions would be very in-teresting:

allEmpty _ = FalseallEmpty [] = TruealwaysEven n

| otherwise = False| n `div` 2 == 0 = True

<b>Record Syntax</b> Normally pattern matching curs based on the position of arguments in thevalue being matched. Types declared with recordsyntax, however, can match based on those recordnames. Given this data type:

oc-data Color = C { red, green

, blue :: Int }we can match ongreen only:

isGreenZero (C { green = 0 }) = TrueisGreenZero _ = False

Argument capture is possible with this syntax,though it gets clunky. Continuing the above, nowdefine aPixel type and a function to replace valueswith non-zerogreen components with all black:

data Pixel = P Color

-- Color value untouched if green is 0setGreen (P col@(C { green = 0 })) = P colsetGreen _ = P (C 0 0 0)

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

<b>Lazy Patterns</b> This syntax, also known as refutable patterns, allows pattern matches which al-ways succeed. That means any clause using thepattern will succeed, but if it tries to actually usethe matched value an error may occur. This is gen-erally useful when an action should be taken onthe type of a particular value, even if the value isn’tpresent.

ir-For example, define a class for default values:class Def a where

defValue :: a -> a

The idea is you givedefValue a value of the righttype and it gives you back a default value for thattype. Defining instances for basic types is easy:

instance Def Bool wheredefValue _ = Falseinstance Def Char where

defValue _ = ' '

Maybe is a littler trickier, because we want to geta default value for the type, but the constructormight beNothing. The following definition wouldwork, but it’s not optimal since we get NothingwhenNothing is passed in.

instance Def a => Def (Maybe a) wheredefValue (Just x) = Just (defValue x)defValue Nothing = Nothing

We’d rather get aJust (default value) back instead.Here is where a lazy pattern saves us – we can pre-tend that we’ve matchedJust x and use that to geta default value, even ifNothing is given:

instance Def a => Def (Maybe a) wheredefValue ~(Just x) = Just (defValue x)

As long as the value x is not actually evaluated,we’re safe. None of the base types need to look atx(see the “_” matches they use), so things will workjust fine.

One wrinkle with the above is that we mustprovide type annotations in the interpreter or thecode when using a Nothing constructor. Nothinghas typeMaybe a but, if not enough other informa-tion is available, Haskell must be told what a is.Some example default values:

-- Return "Just False"

defMB = defValue (Nothing :: Maybe Bool)-- Return "Just ' '"

defMC = defValue (Nothing :: Maybe Char)

<b>List Comprehensions</b>

A list comprehension consists of three types of ements - generators, guards, and targets. A list com-prehension creates a list of target values based onthe generators and guards given. This comprehen-sion generates all squares:

el-squares = [x * x | x <- [1..]]

x <- [1..] generates a list of all Integer valuesand puts them inx, one by one. x * x creates eachelement of the list by multiplyingx by itself.

Guards allow certain elements to be excluded.The following shows how divisors for a given num-ber (excluding itself) can be calculated. Notice howd is used in both the guard and target expression.

divisors n =

[d | d <- [1..(n `div` 2)], n `mod` d == 0]

Comprehensions are not limited to numbers. Anylist will do. All upper case letters can be generated:

ups =

[c | c <- [minBound .. maxBound], isUpper c]

Or to find all occurrences of a particular breakvaluebr in a list word (indexing from 0):

There are very few predefined “operators” inHaskell - most that do look predefined are actu-ally syntax (e.g., “=”). Instead, operators are simplyfunctions that take two arguments and have specialsyntax support. Any so-called operator can be ap-plied as a normal function using parentheses:

3 + 4 == (+) 3 4

To define a new operator, simply define it as a mal function, except the operator appears betweenthe two arguments. Here’s one which takes insertsa comma between two strings and ensures no extraspaces appear:

</div>

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×