Send Beginners mailing list submissions to
        [email protected]

To subscribe or unsubscribe via the World Wide Web, visit
        http://www.haskell.org/mailman/listinfo/beginners
or, via email, send a message with subject or body 'help' to
        [email protected]

You can reach the person managing the list at
        [email protected]

When replying, please edit your Subject line so it is more specific
than "Re: Contents of Beginners digest..."


Today's Topics:

   1. Re:  question on typeclasses and applicatives (Daniel Fischer)
   2. Re:  randomize the order of a list (Gabriel Scherer)
   3. Re:  randomize the order of a list (Gabriel Scherer)
   4. Re:  question on typeclasses and applicatives (Alec Benzer)
   5.  Graham Scan exercise from Chapter 3 RWH -Spoilers. Don't
      read if you want to do this exercise. (Michael Litchard)
   6. Re:  Ranges and List Comprehensions in SQL (Toby Thain)


----------------------------------------------------------------------

Message: 1
Date: Fri, 3 Sep 2010 00:49:01 +0200
From: Daniel Fischer <[email protected]>
Subject: Re: [Haskell-beginners] question on typeclasses and
        applicatives
To: Alec Benzer <[email protected]>
Cc: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain;  charset="iso-8859-1"

On Friday 03 September 2010 00:17:22, Alec Benzer wrote:
> I guess I'm still sort of confused or perturbed with why it's disabled
> by default. If the compiler has the ability to do it and there are no
> problems with doing it, why not just allow it without requiring you to
> pass a flag to the compiler?

It's disabled by default because the language standard laid down different 
rules. Generally, compiler writers tend to avoid enabling too much non-
standard behaviour by default (though chapter 12 of the GHC user's guide 
lists a couple of deviations). However, often there's useful stuff that 
goes against the standard (well, it seemed to be a good idea at the time), 
so many compilers offer extensions to go beyond the standard.


------------------------------

Message: 2
Date: Thu, 2 Sep 2010 15:34:07 +0200
From: Gabriel Scherer <[email protected]>
Subject: Re: [Haskell-beginners] randomize the order of a list
To: [email protected]
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

I must apologize : a part of my post about quicksort didn't make sense
: if one choose the pivot position randomly, elements shouldn't be
splitted with even probability, because there would be no control over
the size of the results list.

If I understand it correctly, your solution doesn't pick a pivot
position, but dynamically adapt list size probabilities during
traversal.

I have a different solution, that pick a pivot position, then choose
the elements with carefully weighted probability to get the right
left-hand and right-hand sizes. The key idea comes from your analysis
(more precisely, from the presence of the n `over` k probabilities) :
for a given k (the pivot), choose a random subset of k elements as the
left-hand side of the pivot.

import Random (Random, StdGen, randomRIO)
import Control.Monad (liftM)

quickshuffle :: [a] -> IO [a]
quickshuffle [] = return []
quickshuffle [x] = return [x]
quickshuffle xs = do
             (ls, rs) <- partition xs
             sls <- quickshuffle ls
             srs <- quickshuffle rs
             return (sls ++ srs)

-- The idea is that to partition a list of length n, we choose a pivot
-- position randomly (1 < k < n), then choose a subset of k elements
-- in the list to be on the left side, and the other n-k on the right
-- side.
--
-- To choose a random subset of length k among n, scan the list and
-- add each element with probability
--   (number of elements left to pick) / (number of elements left to scan)
--
partition :: [a] -> IO ([a], [a])
partition xs = do
          let n = length xs
          k <- randomRIO (1, n-1)
          split n k ([], []) xs
  where split n k (ls, rs) [] = return (ls, rs)
        split n k (ls, rs) (x:xs) = do
            p <- randomRIO (1, n)
            if p <= k
               then split (n - 1) (k - 1) (x:ls, rs) xs
               else split (n - 1) k       (ls, x:rs) xs



I have also written an implementation for my former algorithm :

import Data.List (mapAccumL, sortBy)
import System.Random (RandomGen, split, randoms)
import Data.Ord (Ordering)
import Data.Function (on)
-- compare two real numbers as infinite sequences of booleans
real_cmp :: [Bool] -> [Bool] -> Ordering
real_cmp (True:_) (False:_) = LT
real_cmp (False:_) (True:_) = GT
real_cmp (_:xs) (_:ys) = real_cmp xs ys
-- weight each element with a random real number
weight_list :: RandomGen g => g -> [a] -> [([Bool], a)]
weight_list g = snd . mapAccumL weight g
               where weight g x =
                                let (g1, g2) = split g in
                                (g1, (randoms g2, x))
-- shuffle by sorting on weights
shuffle :: RandomGen g => g -> [a] -> [a]
shuffle g = map snd . sort_on_weights . weight_list g
       where sort_on_weights = sortBy (real_cmp `on` fst)

> Interesting question! Adapting quick sort is not that easy, though.
>
> First, you can skip choosing the pivot position because it is already
> entailed by the choices of elements left and right to it.
>
> Second, probability 1/2 won't work, it doesn't give a uniform
> distribution. In order to get that, the remaining input  xs  has to be
> partitioned into two lists  xs = (ls,rs)  such that
>
>      probability that  length ys == k  is   1/(n `over` k)
>
> where
>
>      n `over` k = n! / (k! * (n-k)!)
>
> is the binomial coefficient. After all, calling "quickrandom"
> recursively on each of the two lists will give two permutations with
> probability  1/k!  and  1/(n-k)!  and the probability for a compound
> permutation is
>
>     1/(n `over` k) * 1/k! * 1/(n-k)! = 1/n!
>
> as desired. In contrast, distributing elements with probability 1/2
> would give
>
>     probability that  length ys == k  is  (n `over` k) * 2^(-n)
>
> which would skew the distribution heavily towards permutations where the
> pivot element is in the middle.
>
>
> However, it is possible to divide the list properly, though I haven't
> worked out the exact numbers. The method would be
>
>      divide (x:xs) = do
>           (ls,rs) <- divide xs
>           random  <- uniform (0, 1) :: Random Double
>           if random <= p (length xs) (length ls)
>               then return (x:ls, rs)
>               else return (ls, x:rs)
>
> where the probability  p  of putting the element  x  into the left part
> has to be chosen such that
>
>     1/(n `over` k) =
>         1/(n-1 `over` k-1) * p (n-1) (k-1)
>       + 1/(n-1 `over` k  ) * (1 - p (n-1) k)
>
>
> Regards,
> Heinrich Apfelmus


------------------------------

Message: 3
Date: Thu, 2 Sep 2010 15:45:23 +0200
From: Gabriel Scherer <[email protected]>
Subject: Re: [Haskell-beginners] randomize the order of a list
To: [email protected]
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

A small remark I forgot.

First, It seems to me that the choice of k need not be random, but
only arbitrary. I believe that any fixed k would work. In particular,
it is interesting to compare this algorithm with k = n/2, and Heinrich
Apfelmus's algorithm based on mergesort (
http://apfelmus.nfshost.com/articles/random-permutations.html ):

- mergeshuffle split the unshuffled list directly, shuffle each part,
then merge them carefully
- quickshuffle split the unshuffled list carefully, shuffle each part,
then merge them directly

mergeshuffle's `merge` and quickshuffle's `split` are very similar
and, in a sense, dual.
`merge` reason on the probability (number of elements in the left list
to merge) / (total number of elements to merge)
`split` reason on the probability (number of elements left to put in
the left list) / (total number of elements left to split)

On Thu, Sep 2, 2010 at 3:34 PM, Gabriel Scherer
<[email protected]> wrote:
> I must apologize : a part of my post about quicksort didn't make sense
> : if one choose the pivot position randomly, elements shouldn't be
> splitted with even probability, because there would be no control over
> the size of the results list.
>
> If I understand it correctly, your solution doesn't pick a pivot
> position, but dynamically adapt list size probabilities during
> traversal.
>
> I have a different solution, that pick a pivot position, then choose
> the elements with carefully weighted probability to get the right
> left-hand and right-hand sizes. The key idea comes from your analysis
> (more precisely, from the presence of the n `over` k probabilities) :
> for a given k (the pivot), choose a random subset of k elements as the
> left-hand side of the pivot.
>
> import Random (Random, StdGen, randomRIO)
> import Control.Monad (liftM)
>
> quickshuffle :: [a] -> IO [a]
> quickshuffle [] = return []
> quickshuffle [x] = return [x]
> quickshuffle xs = do
>             (ls, rs) <- partition xs
>             sls <- quickshuffle ls
>             srs <- quickshuffle rs
>             return (sls ++ srs)
>
> -- The idea is that to partition a list of length n, we choose a pivot
> -- position randomly (1 < k < n), then choose a subset of k elements
> -- in the list to be on the left side, and the other n-k on the right
> -- side.
> --
> -- To choose a random subset of length k among n, scan the list and
> -- add each element with probability
> --   (number of elements left to pick) / (number of elements left to scan)
> --
> partition :: [a] -> IO ([a], [a])
> partition xs = do
>          let n = length xs
>          k <- randomRIO (1, n-1)
>          split n k ([], []) xs
>  where split n k (ls, rs) [] = return (ls, rs)
>        split n k (ls, rs) (x:xs) = do
>            p <- randomRIO (1, n)
>            if p <= k
>               then split (n - 1) (k - 1) (x:ls, rs) xs
>               else split (n - 1) k       (ls, x:rs) xs
>
>
>
> I have also written an implementation for my former algorithm :
>
> import Data.List (mapAccumL, sortBy)
> import System.Random (RandomGen, split, randoms)
> import Data.Ord (Ordering)
> import Data.Function (on)
> -- compare two real numbers as infinite sequences of booleans
> real_cmp :: [Bool] -> [Bool] -> Ordering
> real_cmp (True:_) (False:_) = LT
> real_cmp (False:_) (True:_) = GT
> real_cmp (_:xs) (_:ys) = real_cmp xs ys
> -- weight each element with a random real number
> weight_list :: RandomGen g => g -> [a] -> [([Bool], a)]
> weight_list g = snd . mapAccumL weight g
>                where weight g x =
>                                 let (g1, g2) = split g in
>                                 (g1, (randoms g2, x))
> -- shuffle by sorting on weights
> shuffle :: RandomGen g => g -> [a] -> [a]
> shuffle g = map snd . sort_on_weights . weight_list g
>        where sort_on_weights = sortBy (real_cmp `on` fst)
>
>> Interesting question! Adapting quick sort is not that easy, though.
>>
>> First, you can skip choosing the pivot position because it is already
>> entailed by the choices of elements left and right to it.
>>
>> Second, probability 1/2 won't work, it doesn't give a uniform
>> distribution. In order to get that, the remaining input  xs  has to be
>> partitioned into two lists  xs = (ls,rs)  such that
>>
>>      probability that  length ys == k  is   1/(n `over` k)
>>
>> where
>>
>>      n `over` k = n! / (k! * (n-k)!)
>>
>> is the binomial coefficient. After all, calling "quickrandom"
>> recursively on each of the two lists will give two permutations with
>> probability  1/k!  and  1/(n-k)!  and the probability for a compound
>> permutation is
>>
>>     1/(n `over` k) * 1/k! * 1/(n-k)! = 1/n!
>>
>> as desired. In contrast, distributing elements with probability 1/2
>> would give
>>
>>     probability that  length ys == k  is  (n `over` k) * 2^(-n)
>>
>> which would skew the distribution heavily towards permutations where the
>> pivot element is in the middle.
>>
>>
>> However, it is possible to divide the list properly, though I haven't
>> worked out the exact numbers. The method would be
>>
>>      divide (x:xs) = do
>>           (ls,rs) <- divide xs
>>           random  <- uniform (0, 1) :: Random Double
>>           if random <= p (length xs) (length ls)
>>               then return (x:ls, rs)
>>               else return (ls, x:rs)
>>
>> where the probability  p  of putting the element  x  into the left part
>> has to be chosen such that
>>
>>     1/(n `over` k) =
>>         1/(n-1 `over` k-1) * p (n-1) (k-1)
>>       + 1/(n-1 `over` k  ) * (1 - p (n-1) k)
>>
>>
>> Regards,
>> Heinrich Apfelmus
>


------------------------------

Message: 4
Date: Thu, 2 Sep 2010 20:11:22 -0400
From: Alec Benzer <[email protected]>
Subject: Re: [Haskell-beginners] question on typeclasses and
        applicatives
To: Daniel Fischer <[email protected]>
Cc: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

On Thu, Sep 2, 2010 at 6:49 PM, Daniel Fischer <[email protected]> wrote:
> On Friday 03 September 2010 00:17:22, Alec Benzer wrote:
>> I guess I'm still sort of confused or perturbed with why it's disabled
>> by default. If the compiler has the ability to do it and there are no
>> problems with doing it, why not just allow it without requiring you to
>> pass a flag to the compiler?
>
> It's disabled by default because the language standard laid down different
> rules. Generally, compiler writers tend to avoid enabling too much non-
> standard behaviour by default (though chapter 12 of the GHC user's guide
> lists a couple of deviations). However, often there's useful stuff that
> goes against the standard (well, it seemed to be a good idea at the time),
> so many compilers offer extensions to go beyond the standard.
>

I guess I would then be concerned with why they didn't allow it in the
standard (though I guess "well, it seemed to be a good idea at the
time" answers that).

I think I also would want to avoid doing things not in the language
standard on principle, since my instinct would tell me that if only a
particular compiler implements, I shouldn't use it because it'll
produce non-standard code. Though this sort of comes comes from C/C++
where there are different compilers on different platforms, but I
guess with haskell people pretty much use ghc everywhere?


------------------------------

Message: 5
Date: Thu, 2 Sep 2010 17:21:45 -0700
From: Michael Litchard <[email protected]>
Subject: [Haskell-beginners] Graham Scan exercise from Chapter 3 RWH
        -Spoilers. Don't read if you want to do this exercise.
To: [email protected]
Message-ID:
        <[email protected]>
Content-Type: text/plain; charset=ISO-8859-1

Below is my solution for the Graham Scan Algorithm.
I tried to limit myself to information in the first three chapters
while completing this problem.
Now, I want to remove the explicit recursion and use more idiomatic
Haskell. I'd like to get some advice on which functions/modules would
be helpful in this.


<code>
data Direction = DStraight
               | DLeft
               | DRight
                 deriving (Eq,Show)

type PointXY = (Double,Double)

calcTurn :: PointXY -> PointXY -> PointXY -> Direction
calcTurn a b c
        | crossProduct == 0 = DStraight
        | crossProduct > 0  = DLeft
        | otherwise         = DRight
       where crossProduct = ((fst b - fst a) * (snd c - snd a)) -
((snd b - snd a) * (fst c - fst a))

calcDirectionList :: [PointXY] -> [Direction]
calcDirectionList (x:y:z:zs) = (calcTurn x y z) : (calcDirectionList (y:z:zs))
calcDirectionList _ = []

sortListByY :: [PointXY] -> [PointXY]
sortListByY [] = []
sortListByY [a] = [a]
sortListByY (a:as) = insert (sortListByY as)
           where insert [] = [a]
                 insert (b:bs) | snd a <= snd b = a : b : bs
                               | otherwise      = b : insert bs

sortListByCoTangent :: [PointXY] -> [PointXY]
sortListByCoTangent [] = []
sortListByCoTangent [a] = [a]
sortListByCoTangent (a:as) = a : insert (sortListByCoTangent as)
                 where insert :: [PointXY] -> [PointXY]
                       insert [b] = [b]
                       insert (b:c:cs) | (myCoTan a b) >= (myCoTan a
c) =  b : insert (c:cs)
                                       | otherwise
 =  c : insert (b:cs)
                             where myCoTan :: PointXY -> PointXY -> Double
                                   myCoTan p1 p2 = (fst p2 - fst p1) /
(snd p2 - snd p1)


createHull :: [PointXY] -> [PointXY]
createHull (a:b:c:cs) = a : filterPoint (createHull (b:c:cs))
        where filterPoint :: [PointXY] -> [PointXY]
              filterPoint (x:y:z:zs)
                        | calcTurn x y z == (DLeft)     = [x] ++ [y]
++ [z] ++ filterPoint (zs)
                        | otherwise                     = [x] ++ [z]
++ filterPoint (zs)
              filterPoint [x] = a:[x]
              filterPoint _ = []
createHull _ = []

main :: IO ()
main = do
  let points = [(5.0,0.0),(5.0,6.0),(3.0,-4.2),(0.0,6.0)]
  print $ createHull points


------------------------------

Message: 6
Date: Thu, 2 Sep 2010 20:34:35 -0400
From: Toby Thain <[email protected]>
Subject: Re: [Haskell-beginners] Ranges and List Comprehensions in SQL
To: Tom Murphy <[email protected]>
Cc: [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset="us-ascii"


On 2-Sep-10, at 3:54 PM, Tom Murphy wrote:

> Hi Everyone,
>
>      Is there a "From SQL"-type function that "restores" a range or  
> list comprehension?
>
> Example:
> Let's say I want to keep track of which episodes of a TV show I've  
> seen. I have an SQL table, in which is a record:
>    id (INTEGER): 30
>    title (VARCHAR): "The Simpsons"
>    episodes_watched (Some data format): [1..4], [14], [18..21],  
> [23..25]

To model this relationally, episodes_watched cannot be some kind of  
composite value. You need two relations, for example:

CREATE TABLE tv_show (
        id INTEGER NOT NULL, -- etc
        title VARCHAR(80),
        PRIMARY KEY (id)
);
CREATE TABLE episodes_watched (
        tv_show_id INTEGER NOT NULL,
        episode_number INTEGER,
        PRIMARY KEY (tv_show_id, episode_number)
        -- and ideally a foreign key constraint
);

Data:

tv_show (30, "The Simpsons")
episodes_watched (30,1), (30,2), (30,3), (30,4), (30,14), (30,18),  
(30,19), (30,20), (30,21), (30,23), (30,24), (30,25)

--Toby


>
> Then, when I pull the record, in Haskell, the "Episodes Watched" is  
> already one list:
> [1,2,3,4,14,18,19,21,23,24,25]
>
> , or a series of lists that I can append together:
> [1,2,3,4], [14], [18,19,20,21], [23,24,25]
>
>
> Note in the example that I would like to be able to store multiple  
> ranges within a single record.
>
>
> Thanks so much for any help!
> Tom
> _______________________________________________
> Beginners mailing list
> [email protected]
> http://www.haskell.org/mailman/listinfo/beginners

-------------- next part --------------
An HTML attachment was scrubbed...
URL: 
http://www.haskell.org/pipermail/beginners/attachments/20100902/53f4dfbc/attachment.html

------------------------------

_______________________________________________
Beginners mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/beginners


End of Beginners Digest, Vol 27, Issue 7
****************************************

Reply via email to