HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Simple quotes app

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
simplequotesapp

Problem

I created simple app that read ~ delimited txt file, convert it to [String] and display quotes on screen.

  • How would you improve this code? To be more Haskell like...



  • in splitStr (c/=) is faster than (/=c) by 20-50 ns. Why?



  • Any other ways to clear screen in windows console app?



  • If we compile this code as win console app, I assume threadDelay pauses whole app not just outQ function. Right?



  • Can someone point me in right direction how to refactor this code to use someting like JavaScripts setInterval or setTimeout.



  • is there some easy way to catch ctrl+c so I can showCursor on exit?



.

import System.IO
import System.Random
import Control.Concurrent (threadDelay) -- microseconds
import System.Console.ANSI      -- clearScreen

fileName = "quotes.txt"
oneSecond = 1000000
delay = 1   -- sec

splitStr _ [] = []
splitStr c xs = takeWhile (c/=) xs : splitStr c (drop 1 $ dropWhile (c/=) xs)

--clear = putStr "\ESC[2J"      -- not working on windows

outQ list l sec g = do
  clearScreen
  setCursorPosition 0 0    -- row col
  let (index, gen') = randomR (0, l) g
  putStrLn $ list !! index

  threadDelay $ sec * oneSecond
  outQ list l delay gen'  -- works

main :: IO ()
main = do
  setTitle "Quotes"
  hideCursor             -- catch ctrl+c for showCursor
  gen <- newStdGen    
  str <- readFile fileName
  let qlist = splitStr '~' str
  let len = length qlist -1

  outQ qlist len delay gen

Solution

I'm going to talk about your splitStr function.

First, it's almost the same as splitOn from Data.List.Split in the split package.

  • Run cabal install split



  • Add import Data.List.Split at the start of your code



-
Replace

let qlist = splitStr '~' str


with

let qlist = splitOn "~" str


But let's suppose you can't use the split package for some reason, and try to improve your splitStr.

splitStr :: Eq a => a -> [a] -> [[a]]
splitStr _ [] = []
splitStr c xs = takeWhile (c/=) xs : splitStr c (drop 1 $ dropWhile (c/=) xs)


We can use span instead of takeWhile and dropWhile. This is clearer and also more efficient as we only have to traverse the list once instead of twice.

splitStr :: Eq a => a -> [a] -> [[a]]
splitStr _ [] = []
splitStr c xs = ys : splitStr c (drop 1 zs)
  where (ys, zs) = split (c/=) xs

Code Snippets

let qlist = splitStr '~' str
let qlist = splitOn "~" str
splitStr :: Eq a => a -> [a] -> [[a]]
splitStr _ [] = []
splitStr c xs = takeWhile (c/=) xs : splitStr c (drop 1 $ dropWhile (c/=) xs)
splitStr :: Eq a => a -> [a] -> [[a]]
splitStr _ [] = []
splitStr c xs = ys : splitStr c (drop 1 zs)
  where (ys, zs) = split (c/=) xs

Context

StackExchange Code Review Q#13888, answer score: 4

Revisions (0)

No revisions yet.