Tokens not in order for tokenization / lexer (kotlin) - loops

I am creating a tokenization system in Kotlin / JVM that takes in a file and returns each char or sequence of chars as a token. For some reason, whenever I tokenized a string, it finds the second instance of s "string" token before moving onto the next token, or in other words, the tokens are not in order. I think it might have to do with the loop, but I just can't figure it out. I am still learning Kotlin, so if anyone could give me pointers as well, that'd be great! Much appreciated any help.
output of tokens :
[["chello", string], ["tomo", string], [:, keyV], ["hunna", string], ["moobes", string], ["hunna", string]]
My file looks like this.
STORE "chello" : "tomo" as 1235312
SEND "hunna" in Hollo
GET "moobes"
GET "hunna"
fun tokenCreator (file: BufferedReader) {
var lexicon : String = file.readText()
val numRegex = Regex("^[1-9]\\d*(\\.\\d+)?\$")
val dataRegex = Regex("[(){}]")
val token = mutableListOf<List<Any>>()
for((index, char) in lexicon.withIndex()) {
println(char)
when {
char.isWhitespace() -> continue
char.toString() == ":" -> token.add(listOf(char.toString(), "keyV") )
char.toString().matches(Regex("[()]")) -> token.add(listOf(char, "group") )
char.toString().matches(dataRegex) -> token.add(listOf(char, "data_group" ) )
char == '>' -> token.add(listOf(char.toString(), "verbline") )
char == '"' -> {
var stringOf = ""
val firstQuote = lexicon.indexOf(char)
val secondQuote = lexicon.indexOf(char, firstQuote + 1)
if(firstQuote == -1 || secondQuote == -1) {
break
}
for(i in firstQuote..secondQuote) {
stringOf += lexicon[i]
}
lexicon = lexicon.substring(secondQuote + 1, lexicon.length)
token.add(listOf(stringOf, "string"))
}
}
}
println(token)
}

Changing the content while iterating seems like a recipe for confusion...
And you don't seem to increment the index to skip over consumed content. I'd recommend to change the loop in a way that allows you to skip over content you have consumed
I'd also remove this line:
lexicon = lexicon.substring(secondQuote + 1, lexicon.length)
Then replace
val firstQuote = lexicon.indexOf(char)
with
val firstQuote = index
You can also use substring instead of iteration for stringOf
val stringOf = lexicon.substring(
Moreover, using toString to check for ':' seems inefficient

Related

collect bytes from string

This is decoded string from bytes, they are always different. Am not using it in the code, its just for shown what is all about.
"Random String; Tags:Value1:1,Value:2,Value3:value4"
This is array of bytes from above string which i get as input.
[&u8...&u8]
What i need is get the values fromthose. While every byte in the array is changing. but some bytes are always same. I was thinking if there is any way how to extract it without using any Strings... Thanks for any ideas
so the output would look like this:
let v1 = [&u8, &u8, &u8, &u8, &u8];
let v2 = [&u8, &u8];
let v3 = [&u8];
let v4 = [&u8];
let v5 = [&u8];
You can do all this without allocating any extra space using rusts iterators, using split and related functions
At the top level, your data is of the form (key:value;)*
This suggests first splitting on ;
Then splitting each of these pieces into key and value using :
In your case, all the information is when the key is "tags".
Then within the tags section, you again have (mostly) key-value pairs of the form (key-value,)* so we need to split on , then break into key-value pairs using -.
An example that does this but only prints all the tag key-value pairs is:
fn split_kv(v: &[u8], c: u8) -> Option<(&[u8], &[u8])> {
let n = v.iter().position(|&b| b == c)?;
let w = v.split_at(n);
Some((w.0, &(w.1)[1..]))
}
fn main() {
let s: &str = "Background:Sunfire Topo;Base:Eagle;Accessory3:None;Patch:Oreo;Jacket:Pink Bonez;Eyes:BloodShot;Beak:Drool;Accessory2:Nose Ring;Accessory1:None;Item:Knife;tags:Dope Eagles,ELEMENT-HYDRO,ATTACK-10,DEFENSE-5,HIGHNESS-4,SWAG-1;metadata:QmU7JcFDoGcUvNkDgsPz9cy13md4xHdNyD6itwmgVLuo7x/860.json";
let ss: &[u8] = s.as_bytes();
let tags = ss
.split(|&b| b == b';') // Break up at ';'
.filter_map(|s| split_kv(s, b':')) // Split each piece into key-value pairs using ':'
.filter_map(|(k, v)| { // Only keep the tags entry.
if k == "tags".as_bytes() {
Some(v)
} else {
None
}
})
.next() // And just take the first of those.
.unwrap();
// Split the tags by ','
for t in tags.split(|&b| b == b',') {
// Then try to convert each to a key-value using '-' as seperator.
if let Some((k, v)) = split_kv(t, b'-') {
println!(
"k={:?} v={:?}",
std::str::from_utf8(k).unwrap(),
std::str::from_utf8(v).unwrap()
);
} else {
println!("t={:?}", std::str::from_utf8(t).unwrap());
}
}
}
You can run this here

Always getting the last item in a list using split

As of right now i retrieve the first item in my list that i generate by using split, but i also want the last item in a seperate function.
The problem ist the list is generated from an ocr and is never the same length or size.
is there a way in kotlin to always get the last item?
This is the code for getting the first :
fun String.firstLine(): String {
if (this.isEmpty()) return ""
return this.split("\n").get(0)
}
You can do something like this:
fun String.firstAndLast() = split("\n").let { it.first() to it.last() }
fun main() {
val s = "first line\nsecond line\nlast line"
val (first, last) = s.firstAndLast()
println(first)
println(last)
}
output is:
first line
last line
The fun will split, then use the resulting list and get only the first and last element, returning a Pair<String, String>, which can be easily deconstructed with val (first, last), then those values can be used separately.
Edit: for the penultimate as well, I don't think that a list might be a good idea. I personally would go with an object of sorts, or with a Triple.
Class way:
class NoNameIdeas(
val first: String = "",
val penultimate: String = "",
val last: String = ""
) {
companion object {
fun fromString(string: String): NoNameIdeas {
val l = string.split("\n")
val first = l.first()
return when (l.size) {
1 -> NoNameIdeas(first = first)
2 -> NoNameIdeas(first = first, penultimate = "what should this be?", last = l.last())
else -> NoNameIdeas(first = first, penultimate = l[l.lastIndex - 1], last = l.last())
}
}
}
}
fun main() {
val string = "first line\nsecond line\nsecond last\nlast line"
val result = NoNameIdeas.fromString(string)
println(result.first)
println(result.penultimate)
println(result.last)
}
As you can guess, I have no idea how to name this object. Another issues is, what should we do with the penultimate, if we only have 2 lines in total? It can be the same as the first, or it can be empty, or it can be the same as the last. That is your decision, it also might never happen, only you know what data you have.
Another way of doing it without a class, but by using a triple:
Triple way:
fun main() {
val string = "first line\nsecond line\nsecond last\nlast line"
val result = string.firstPenultimateAndLast()
println(result.first)
println(result.second)
println(result.third)
}
fun String.firstPenultimateAndLast(): Triple<String, String, String> {
val l = split("\n")
val first = l.first()
var penultimate = "" //same as the class, what happens when we only have 2 lines?
var last = l.first() //if we only have 1, then I guess that the first is the last as well, will get a new value otherwise
when (l.size) {
2 -> last = l.last()
else -> {
penultimate = l[lastIndex - 1]
last = l.last()
}
}
return Triple(first, penultimate, last)
}

swift calling item from an array with a string [duplicate]

How to concatenate string in Swift?
In Objective-C we do like
NSString *string = #"Swift";
NSString *resultStr = [string stringByAppendingString:#" is a new Programming Language"];
or
NSString *resultStr=[NSString stringWithFormat:#"%# is a new Programming Language",string];
But I want to do this in Swift-language.
You can concatenate strings a number of ways:
let a = "Hello"
let b = "World"
let first = a + ", " + b
let second = "\(a), \(b)"
You could also do:
var c = "Hello"
c += ", World"
I'm sure there are more ways too.
Bit of description
let creates a constant. (sort of like an NSString). You can't change its value once you have set it. You can still add it to other things and create new variables though.
var creates a variable. (sort of like NSMutableString) so you can change the value of it. But this has been answered several times on Stack Overflow, (see difference between let and var).
Note
In reality let and var are very different from NSString and NSMutableString but it helps the analogy.
You can add a string in these ways:
str += ""
str = str + ""
str = str + str2
str = "" + ""
str = "\(variable)"
str = str + "\(variable)"
I think I named them all.
var language = "Swift"
var resultStr = "\(language) is a new programming language"
This will work too:
var string = "swift"
var resultStr = string + " is a new Programming Language"
\ this is being used to append one string to another string.
var first = "Hi"
var combineStr = "\(first) Start develop app for swift"
You can try this also:- + keyword.
var first = "Hi"
var combineStr = "+(first) Start develop app for swift"
Try this code.
let the_string = "Swift"
let resultString = "\(the_string) is a new Programming Language"
Very Simple:
let StringA = "Hello"
let StringB = "World"
let ResultString = "\(StringA)\(StringB)"
println("Concatenated result = \(ResultString)")
You can now use stringByAppendingString in Swift.
var string = "Swift"
var resultString = string.stringByAppendingString(" is new Programming Language")
Xcode didn't accept optional strings added with a normal string. I wrote this extensions to solve that problem:
extension String {
mutating func addString(str: String) {
self = self + str
}
}
Then you can call it like:
var str1: String?
var str1 = "hi"
var str2 = " my name is"
str1.addString(str2)
println(str1) //hi my name is
However you could now also do something like this:
var str1: String?
var str1 = "hi"
var str2 = " my name is"
str1! += str2
It is called as String Interpolation.
It is way of creating NEW string with CONSTANTS, VARIABLE, LITERALS and EXPRESSIONS.
for examples:
let price = 3
let staringValue = "The price of \(price) mangoes is equal to \(price*price) "
also
let string1 = "anil"
let string2 = "gupta"
let fullName = string1 + string2 // fullName is equal to "anilgupta"
or
let fullName = "\(string1)\(string2)" // fullName is equal to "anilgupta"
it also mean as concatenating string values.
Hope this helps you.
I just switched from Objective-C to Swift (4), and I find that I often use:
let allWords = String(format:"%# %# %#",message.body!, message.subject!, message.senderName!)
Swift 5
You can achieve it using appending API. This returns a new string made by appending a given string to the receiver.
API Details : here
Use:
var text = "Hello"
text = text.appending(" Namaste")
Result:
Hello
Hello Namaste
To print the combined string using
Println("\(string1)\(string2)")
or String3 stores the output of combination of 2 strings
let strin3 = "\(string1)\(string2)"
One can also use stringByAppendingFormat in Swift.
var finalString : NSString = NSString(string: "Hello")
finalString = finalString.stringByAppendingFormat("%#", " World")
print(finalString) //Output:- Hello World
finalString = finalString.stringByAppendingFormat("%#", " Of People")
print(finalString) //Output:- Hello World Of People
Concatenation refers to the combining of Strings in Swift. Strings may contain texts, integers, or even emojis! There are many ways to String Concatenation. Let me enumerate some:
Same String
Using +=
This is useful if we want to add to an already existing String. For this to work, our String should be mutable or can be modified, thus declaring it as a Variable. For instance:
var myClassmates = "John, Jane"
myClassmates += ", Mark" // add a new Classmate
// Result: "John, Jane, Mark"
Different Strings
If we want to combine different Strings together, for instance:
let oldClassmates = "John, Jane"
let newClassmate = "Mark"
We can use any of the following:
1) Using +
let myClassmates = oldClassmates + ", " + newClassmate
// Result: "John, Jane, Mark"
Notice that the each String may be a Variable or a Constant. Declare it as a Constant if you're only gonna change the value once.
2) String Interpolation
let myClassmates = "\(oldClassmates), \(newClassmate)"
// Result: "John, Jane, Mark"
3) Appending
let myClassmates = oldClassmates.appending(newClassmate)
// Result: "John, Jane, Mark"
Refer to Strings & Characters from the Swift Book for more.
Update: Tested on Swift 5.1
Swift 4.2
You can also use an extension:
extension Array where Element == String? {
func compactConcate(separator: String) -> String {
return self.compactMap {
if let unwrappedString = $0,
unwrappedString.isEmpty {
return nil
} else {
return $0
}
}
.joined(separator: separator)
}
}
Use:
label.text = [m.firstName, m.lastName].compactConcate(separator: " ")
Result:
"The Man"
"The"
"Man"
From: Matt Neuburg Book “iOS 13 Programming Fundamentals with Swift.” :
To combine (concatenate) two strings, the simplest approach is to use the + operator:
let s = "hello"
let s2 = " world"
let greeting = s + s2
This convenient notation is possible because the + operator is overloaded: it does one thing when the operands are numbers (numeric addition) and another when the operands are strings (concatenation).
The + operator comes with a += assignment shortcut; naturally, the variable on the left side must have been declared with var:
var s = "hello"
let s2 = " world"
s += s2
As an alternative to +=, you can call the append(_:) instance method:
var s = "hello"
let s2 = " world"
s.append(s2)
Another way of concatenating strings is with the joined(separator:) method. You start with an array of strings to be concatenated, and hand it the string that is to be inserted between all of them:
let s = "hello"
let s2 = "world"
let space = " "
let greeting = [s,s2].joined(separator:space)
Swift 5:
Array of strings into a Single string
let array = ["Ramana","Meharshi","Awareness","Oneness","Enlightnment","Nothing"]
let joined = array.joined(separator: ",")
Swift concatenate strings
Several words about performance
UI Testing Bundle on iPhone 7(real device), iOS 14, -Onone(debug, without optimizations)[About]
var result = ""
for i in 0...count {
<concat_operation>
}
Count = 5_000
//Append
result.append(String(i)) //0.007s 39.322kB
//Plus Equal
result += String(i) //0.006s 19.661kB
//Plus
result = result + String(i) //0.130s 36.045kB
//Interpolation
result = "\(result)\(i)" //0.164s 16.384kB
//NSString
result = NSString(format: "%#%i", result, i) //0.354s 108.142kB
//NSMutableString
result.append(String(i)) //0.008s 19.661kB
Disable next tests:
Plus up to 100_000 ~10s
interpolation up to 100_000 ~10s
NSString up to 10_000 -> memory issues
Count = 1_000_000
//Append
result.append(String(i)) //0.566s 5894.979kB
//Plus Equal
result += String(i) //0.570s 5894.979kB
//NSMutableString
result.append(String(i)) //0.751s 5891.694kB
*Note about Convert Int to String
Source code
import XCTest
class StringTests: XCTestCase {
let count = 1_000_000
let metrics: [XCTMetric] = [
XCTClockMetric(),
XCTMemoryMetric()
]
let measureOptions = XCTMeasureOptions.default
override func setUp() {
measureOptions.iterationCount = 5
}
func testAppend() {
var result = ""
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result.append(String(i))
}
}
}
func testPlusEqual() {
var result = ""
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result += String(i)
}
}
}
func testPlus() {
var result = ""
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result = result + String(i)
}
}
}
func testInterpolation() {
var result = ""
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result = "\(result)\(i)"
}
}
}
//Up to 10_000
func testNSString() {
var result: NSString = ""
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result = NSString(format: "%#%i", result, i)
}
}
}
func testNSMutableString() {
let result = NSMutableString()
measure(metrics: metrics, options: measureOptions) {
for i in 0...count {
result.append(String(i))
}
}
}
}
You could use SwiftString (https://github.com/amayne/SwiftString) to do this.
"".join(["string1", "string2", "string3"]) // "string1string2string"
" ".join(["hello", "world"]) // "hello world"
DISCLAIMER: I wrote this extension
In Swift 5 apple has introduces Raw Strings using # symbols.
Example:
print(#"My name is "XXX" and I'm "28"."#)
let name = "XXX"
print(#"My name is \#(name)."#)
symbol # is necessary after \. A regular \(name) will be interpreted as characters in the string.

Closest-match string-array sorting in Swift

Using Swift4, I would like to sort a string-array according to the closest match to a given searchTerm. Important is to me that if the searchTerm can be found as an exact-match, then the returnArray should show this searchTerm upfront !
Example: Given the Array = ["Hello world", "Hello Jamaica", "Hello", "Family", "Hel"]
And the searchTerm = "Hello", the algorithm should return:
["Hello", "Hello world", "Hello Jamaica", "Hel", "Family"].
Approach 1:
I tried to use FuzzyMatching - and it somehow worked (i.e. it did sort the inputArray according to a given searchTerm, however it did not put the exact-matches upfront ! i.e. With FuzzyMatching I achieved a good sorting according to substring-matches and syntactic sorting. But it did not bring me the exact-matches upfront in the returnArray).
Approach 2:
Then I tried my own algorithm - (see code below). But if there are several strings in the array that all start with my searchTerm (i.e. have searchTerm as a prefix), then somehow my algo does not a good job.
static func bestMatchFilterdStringArray(inputArray: [String], searchTerm: String) -> [String] {
let matchingTerms = inputArray
.filter { $0.range(of: searchTerm, options: .caseInsensitive) != nil }
.sorted { ($0.hasPrefix(searchTerm) ? 0 : 1) < ($1.hasPrefix(searchTerm) ? 0 : 1) }
return matchingTerms
}
How is a "Closest-match string-array sorting" done in Swift4? Especially bringing me exact-matches upfront in the returnArray? Any help appreciated!
You can use Levenshtein distance score to compare your search term with every string in the array, and the one with the highest score will be the first term in your result array etc. Your result will be an array of strings sorted in descending order of the score.
Following extension to string can be used to get Levenshtein distance score. In this algorithm, higher the value, better the equality.
extension String {
func levenshteinDistanceScore(to string: String, ignoreCase: Bool = true, trimWhiteSpacesAndNewLines: Bool = true) -> Double {
var firstString = self
var secondString = string
if ignoreCase {
firstString = firstString.lowercased()
secondString = secondString.lowercased()
}
if trimWhiteSpacesAndNewLines {
firstString = firstString.trimmingCharacters(in: .whitespacesAndNewlines)
secondString = secondString.trimmingCharacters(in: .whitespacesAndNewlines)
}
let empty = [Int](repeating:0, count: secondString.count)
var last = [Int](0...secondString.count)
for (i, tLett) in firstString.enumerated() {
var cur = [i + 1] + empty
for (j, sLett) in secondString.enumerated() {
cur[j + 1] = tLett == sLett ? last[j] : Swift.min(last[j], last[j + 1], cur[j])+1
}
last = cur
}
// maximum string length between the two
let lowestScore = max(firstString.count, secondString.count)
if let validDistance = last.last {
return 1 - (Double(validDistance) / Double(lowestScore))
}
return 0.0
}
}

2 dimension array processing in haskell

Sorry for my question which might seem trivial to some (I'm new). I have a file which contains a map looking like this :
---#--###----
-#---#----##-
------------#
In this file, – characters indicate that you are free to move in this direction. The # character indicates that you cannot move any further in this direction and you should go somewhere else. The # character indicates the location of the treasure. In this case, it is in the bottom right corner, but it could be anywhere in the map. So I have to go through these lines and see if I can reach the #. Here we are starting at the top left corner. So far I have managed to read the content of the file. And I'm wondering how to process this in Haskell. It will be easy in Java using a 2-dimensional array but how can I appproach this problem in Haskell?
For example, for the previous example, the path is:
+++#--###----
-#+--#----##-
--++++++++++#
The + symbol represents the path to the # symbol.
This the algorithm I have to implement it in Java:
Dfs(i,j) {
if (arr[i][j+1] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i,j+1)
} else if(arr[i][j+1] == "#") {
}
if (arr[i][j-1] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i,j-1)
} else if(arr[i][j-1] == "#") {
}
if (arr[i+1][j] == "-" && i >=0 && i<=row.size && j>=0 && j<=column.size) {
Dfs(i+1,j)
} else if(arr[i+1][j] == "#") {
}
}
Thank you
There are many ways of making 2D arrays in Haskell, here is a somewhat laborious example of reading the chars into a Data.Array array, and then moving things about with the so-called state monad:
import Data.Array
import Control.Monad.State.Strict
main = do str <- getContents -- accepts string from stdin
let array = mkThingArray str -- we parse the string
limits = snd (bounds array) -- we remember (height,width)
initialState = ((0::Int,-1::Int),limits,array)
((position,(h,w),a)) <- execStateT findpath initialState
let chars = elems $ fmap toChar a
putStrLn ""
putStrLn $ splitText (w+1) chars
parseArray str = listArray ((0,0),(height-1, width-1)) total where
rawlines = lines str
ls = filter (not . null) rawlines
lens = map length ls
height = length ls
width = minimum lens
proper = map (take width) ls
total = concat proper
data Thing = Open | Closed | Home | Taken deriving (Show, Eq, Ord)
toThing c = case c of '-' -> Open; '#' -> Closed; '#' -> Home;
'+' -> Taken; _ -> error "No such Thing"
toChar c = case c of Open -> '-'; Closed -> '#';
Home -> '#'; Taken -> '+'
mkThingArray str = fmap toThing (parseArray str)
And continuing with an absurdly primitive 'logic' of state change:
-- we begin with moveright, which may then pass on to movedown
-- and so on perhaps in a more sophisticated case
findpath = moveright
where
moveright = do ((n,m), (bound1,bound2), arr) <- get
if m < bound2
then case arr ! (n,m+1) of
Open -> do liftIO (putStrLn "moved right")
put ((n,m+1), (bound1,bound2), arr // [((n,m+1),Taken)])
moveright
Closed -> movedown
Home -> return ()
Taken -> movedown
else movedown
movedown = do ((n,m), (bound1,bound2), arr) <- get
if n < bound1
then case arr ! (n+1,m) of
Open -> do liftIO (putStrLn "moved down")
put ((n+1,m), (bound1,bound2), arr // [((n+1,m),Taken)])
moveright
Closed -> moveright
Home -> return ()
Taken -> moveright
else moveright
splitText n str = unlines $ split n [] str
where split n xss [] = xss
split n xss str = let (a,b) = splitAt n str
in if not (null a)
then split n (xss ++ [a]) b
else xss
which, in this happy case, gives output like this
{-
$ pbpaste | ./arrayparse
moved right
moved right
moved right
moved down
moved right
moved right
moved down
moved right
moved right
moved right
moved right
moved right
moved right
moved right
+++#--###----
-#+++#----##-
----++++++++#
-}
The logic will have to be more sophisticated, with moveleft and moveup, etc., etc. but this is supposed to give the idea, or an idea.
Edit: Here is a version that doesn't use an intermediate type and doesn't throw any IO into the state machine. It should be more usable in ghci, so you can tear it apart more easily:
import Data.Array
import Control.Monad.Trans.State.Strict
main = do str <- readFile "input.txt"
((pos,(h,w),endarray)) <- execStateT findpath
(mkInitialState str)
putStrLn $ prettyArray endarray
-- the following are just synonyms, nothing is happening:
type Pos = (Int, Int) -- Our positions are in 2 dimensions
type Arr = Array Pos Char -- Characters occupy these positions
type ArrState = (Pos, Pos, Arr) -- We will be tracking not just
-- an array of Chars but a
-- current position and the total size
parseArray :: String -> Arr
parseArray str = listArray ((1,1),(height, width)) (concat cropped) where
ls = filter (not . null) (lines str)
width = minimum (map length ls)
height = length ls
cropped = map (take width) ls -- the map is cropped to shortest line
prettyArray :: Arr -> String
prettyArray arr = split [] (elems arr)
where (ab,(h,w)) = bounds arr
split xss [] = unlines xss
split xss str = let (a,b) = splitAt w str
in if null a then unlines xss else split (xss ++ [a]) b
mkInitialState :: String -> ArrState
mkInitialState str = ((1::Int,0::Int), limits, array)
where array = parseArray str -- we parse the string
limits = snd (bounds array) -- we remember (height,width)
-- since we don't resize, tracking this could be avoided
makeStep :: Arr -> Pos -> Arr
makeStep arr (n, m) = arr // [((n,m),'+')] -- this is crude
moveRight, moveDown, findpath :: Monad m => StateT ArrState m ()
moveRight = do ((n,m),bounds,arr) <- get
put ((n,m+1), bounds, makeStep arr (n,m+1))
moveDown = do ((n,m),bounds,arr) <- get
put ((n+1,m), bounds, makeStep arr (n+1,m))
findpath = tryRight
where -- good luck for most paths ...
tryRight = do ((n,m), (_,bound2), arr) <- get
if m < bound2
then case arr ! (n,m+1) of
'#' -> return ()
'-' -> do moveRight
tryRight
_ -> tryDown
else tryDown
tryDown = do ((n,m), (bound1,_), arr) <- get
if n < bound1
then case arr ! (n+1,m) of
'#' -> return ()
'-' -> do moveDown
tryRight
_ -> tryRight
else tryRight
runInput :: String -> String
runInput str = prettyArray endarray
where ((position,(h,w),endarray)) = execState findpath (mkInitialState str)
-- If I wanted to include IO things in the state machine,
-- I would have to use execStateT not execState, which presupposes purity
test :: String -> IO ()
test str = putStrLn (runInput str)
t1 = unlines ["---#--###----"
, ""
, "-#---#----##-"
, ""
, "------------#"
] :: String
--
t2 = unlines ["---#--###----"
,""
,"---#-#----##-"
,""
,"------------#"
] :: String
This very much depends on the way you want to use your 2D array.
If you only care about sequential use, a simple list of lists (basically [[Char]]) may be fine.
If you care about efficient getting to particular random coordinates, I can imagine that an IntList IntList Char could work for you; it's almost like list of lists, but individual cells can be much more efficiently updated, and it gives cheap random access for pathfinding.
Possibly a zipper-like structure would suit you best. I can't (so far) imagine a nice structure of this type that gives you both cheap (O(1) per neighbor cell) navigation for pathfinding and cheap updates.
Also, you could use a mutable map via Monad.Control.State e.g. by keeping a Data.Array in it, but you will have to lift all your logic into this monad (which would complicate passing copies of the map around, when you need it).

Resources