Obsah práce
„Užití funkcionálního programovacího jazyka Haskell k řešení úloh Ústředních kol ČR Soutěže v programování“
Práce SOČ, obor 18: Informatika
„Užití funkcionálního programovacího jazyka Haskell k řešení úloh Ústředních kol ČR Soutěže v programování“
Jednoduchý programovací jazyk na kreslení želví grafiky.
Po písku se pohybuje želva a kreslí za sebou stopu. Uživatel želvu ovládá jednoduchým procedurálním programovacím jazykem.
Základní příkazy:
forward(délka)
right(úhel)
, left(úhel)
pen(tloušťka pera)
color(červená,zelená,modrá)
Řídící struktury:
if(podmínka) { ... }
repeat(počet iterací) { ... }
define jméno_procedury(param1, param2, ...)
{...}
jméno_procedury(arg1, arg2, ...)
split { ... }
Jako výrazy (argumenty příkazů a procedur) lze použít:
+
, -
, *
,
/
(dělení celočíselné, priority a asociativity jako
v matematice),-
).Jediným „typem“ je celé číslo.
define square(side) {
pen(1)
repeat(4) {
forward(side) right(90)
}
pen(0)
}
define row() {
repeat(4) {
square(114) forward(163)
}
forward(-652) right(90)
forward(163) left(90)
}
forward(301) left(90)
forward(301) right(180)
repeat(4) { row() }
define koch(n) {
if(n) {
koch(n-1)
left(60) koch(n-1)
right(120) koch(n-1)
left(60) koch(n-1)
}
if(1-n) {
forward(2)
}
}
pen(0) forward(-175)
left(90) forward(250)
pen(1)
right(120) koch(5)
right(120) koch(5)
right(120) koch(5)
Program je rozdělen na několik částí:
Jednotlivé části jsou víceméně nezávislé.
Krunimir.Ast
Definice typů představující syntaktický strom (AST)
Typ Stmt
reprezentuje příkaz:
data Stmt =
ForwardStmt Expr
| LeftStmt Expr
| RightStmt Expr
| PenStmt Expr
| ColorStmt Expr Expr Expr
| RepeatStmt Expr [Stmt]
| IfStmt Expr [Stmt]
| SplitStmt [Stmt]
| CallStmt String [Expr]
deriving Show
Expr
představuje výraz:
data Expr =
LiteralExpr Integer
| VariableExpr String
| BinopExpr Op Expr Expr
| NegateExpr Expr
deriving Show
data Op = AddOp | SubOp | MulOp | DivOp
deriving Show
Krunimir.Parser
Exportuje jedinou funkci parse
:
parse :: FilePath -> String ->
Either ParseError Program
Založen na PEG gramatice:
program <- space* top-stmt* eof
top-stmt <- define / stmt
define <- "define" space+ identifier
lparen (identifier (comma identifier)*)? rparen
lbrace stmt* rbrace
stmt <- repeat-stmt / if-stmt / split-stmt / proc-stmt
repeat-stmt <- "repeat" lparen expr rparen lbrace stmt* rbrace
if-stmt <- "if" space* lparen expr rparen lbrace stmt* rbrace
split-stmt <- "split" space* lbrace stmt* rbrace
proc-stmt <- "forward" space* lparen expr rparen
/ "left" space* lparen expr rparen
/ "right" space* lparen expr rparen
/ "pen" space* lparen expr rparen
/ "color" space* lparen expr comma expr comma expr rparen
/ identifier space* lparen (expr (comma expr)*)? rparen
expr <- add-expr
add-expr <- mul-expr (add-op space* mul-expr)*
mul-expr <- neg-expr (mul-op space* neg-expr)*
neg-expr <- (neg-op space*)? a-expr
add-op <- "+" / "-"
mul-op <- "*" / "/"
neg-op <- "-"
a-expr <- lit-expr / var-expr / lparen expr rparen
lit-expr <- integer
var-expr <- identifier
integer <- digit+ space*
identifier <- letter alpha-num space*
lparen <- "(" space*
rparen <- ")" space*
lbrace <- "{" space*
rbrace <- "}" space*
comma <- "," space*
digit <- [0-9]
letter <- [a-zA-Z]
alpha-num <- [a-zA-Z0-9]
space <- [ \t\r\n\v\f]
eof <- !.
Implementován pomocí monadických parserů z knihovny
parsec
.
Gramatika PEG:
define <- "define" space+
identifier
lparen (identifier (comma identifier)*)? rparen
lbrace stmt* rbrace
Odpovídající kód Haskellu:
define :: Parser
Define
define = do
string "define"
>>
skipMany1
space
name
<- identifier
params
<- parens
$ identifier
`sepBy`
comma
stmts
<- braces
$ many
stmt
return
$ Define name params stmts
Dílčí parsery se skládají do větších celků...
...užitím vysokoúrovňových kombinátorů.
Z výsledků se sestavuje AST.
Krunimir.Evaluator
Exportuje funkci eval
, která vyhodnotí program:
eval :: Program -> Trace
Trace
představuje stopu želvy:
data Trace
= EmptyTrace
| SplitTrace Trace Trace
| SegmentTrace Segment Trace
deriving Show
data Segment = Segment Point Point Color Int
deriving Show
type Point = (Float,Float)
type Color = (Int,Int,Int)
Hledání nejkratší cesty v bludišti (hradu)
Použitý algoritmus je založen na prohledávání do šířky.
Přechody mezi stavy (x,y)
, konec jakmile dorazíme do cíle.
Přechody mezi stavy (x,y,t)
, konec jakmile dorazíme do cíle.
Dva zvědové s periodou 2:
Stačí sledovat stavy (x,y,t mod p)
Projdeme zdí pouze pokud neexistuje žádná jiná cesta k cíli.
moves :: Bool -> Slice -> Slice -> (Loc,Path) -> [(Loc,Path)]
flood :: Castle -> [Slice] -> STArray s (Int,Loc) (Maybe Path) ->
[(Loc,Path)] -> ST s (Either Path [(Loc,Path)])
navigate :: Castle -> [Slice] -> Bool -> Maybe [Loc]
Zdrojové kódy na serveru GitHub: github.com/honzasp/funsp
Stránky (včetně prezentace) honzasp.github.io/funsp
patek.mail@gmail.com
Wichterlovo gymnázium, Ostrava-Poruba