Advent of Code Day 1 requires looping, in one form or another, over a long string of parentheses like ((((())(())(((()))(( etc. The idea is that ( goes up one "floor", ) goes down one floor, and the objectives are to print
the first index in the string where the floor number is negative and
the final floor when the end of the string is found.
The imperative solution with a for loop is simple (Python as an example):
def main():
flr = 0
basement = False
for idx, elt in enumerate(text):
flr += {
"(": 1,
")": -1
}.get(elt)
if flr < 0 and not basement:
print("first basement pos:", idx + 1)
basement = True
print("final floor:", flr)
The recursive functional solution is a little more complex, but still not too hard.
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return flr
if flr < 0 and not basement:
print("first basement floor index: ", idx + 1)
basement = True
return worker(flr, txt[1:], idx + 1, basement)
def starter(txt):
flr, basement, idx = 0, False, 0
return worker(flr, txt, idx, basement)
if __name__ == '__main__':
__import__("sys").setrecursionlimit(int(1e5))
print("final floor:", starter(text))
Both of these give the correct output of
first basement floor index: 1795
final floor: 74
when run against my challenge input.
except the second one is dumb because Python doesn't have tail call optimisation but never mind that
How can I implement either of these in Factor? This is something I've been confused by ever since I started using Factor.
We can't just use a for loop because there's no equivalent that allows us to keep mutable state between iterations.
We could use a recursive solution:
: day-1-starter ( string -- final-floor )
[ 0 ] dip 0 f day-1-worker 3drop "final floor: %s" printf ;
: day-1-worker
( floor string index basement? -- floor string index basement? )
day-1-worker ! what goes here?
; recursive
Great, that's a skeleton, but what goes in the body of day-1-worker? Factor doesn't have any way to "early return" from a recursive call because there's no way to run the program in reverse and no concept of return -- that doesn't make any sense.
I get the feeling maybe recursion isn't the answer to this question in Factor. If it is, how do I stop recursing?
First of all, recursion is always the answer :)
Since this is a challenge (and I don't know factor), just a hint:
in your python solution you have used the side effect to print the first basement level. Quite unnecessary! You can use basemet argument to hold the floor number too, like this:
def worker(flr, txt, idx, basement):
flr += {"(": 1, ")": -1}[ txt[0] ]
if not (len(txt) - 1): return [flr, basement] # <- return both
if flr < 0 and not basement:
#print("first basement floor index: ", idx + 1) # side effects go away!
basement = idx+1 # <- a number in not False, so that's all
return worker(flr, txt[1:], idx + 1, basement)
So now you get
final,first_basement = worker(0, txt, 0, False)
Or, alternatively you can write 2 functions, first one seeks the index of first basement floor, the other one just computes the final floor. Having <2000 additional small steps is not a big deal even if you do care about performance.
Good luck!
Edit: as of your question concerning recursion in factor, take a look at the Ackermann Function in Factor and the Fibonacci sequence in Factor and you should get the idea how to "break the loop". Actually the only problem is in thinking (emancipate yourself from the imperative model :)); in functional languages there is no "return", just the final value, and stack-based languages you mention are other computational model of the same thing (instead of thinking of folding a tree one thinks about "pushing and poping to/from the stacks" -- which is btw a common way to implement the former).
Edit: (SPOILER!)
I installed Factor and started playing with it (quite nice), for the first question (computing the final score) a possible solution is
: day-1-worker ( string floor -- floor )
dup length 0 =
[ drop ]
[ dup first 40 =
[ swap 1 + ]
[ swap 1 - ]
if
swap rest
day-1-worker ]
if ;
: day-1-starter ( string -- floor )
0 swap day-1-worker ;
So now you can either write similar one for computing basement's index, or (which would be more cool!) to modify it so that it also manages index and basement... (Probably using cond would be wiser than nesting ifs).
You could use the cum-sum combinator:
: to-ups/downs ( str -- seq )
[ CHAR: ( = 1 -1 ? ] { } map-as ;
: run-elevator ( str -- first-basement final-floor )
to-ups/downs cum-sum [ -1 swap index 1 + ] [ last ] bi ;
IN: scratchpad "((())))(())(())(((()))((" run-elevator
--- Data stack:
7
2
EDIT
I originally misread how your were computing the basement value. I've updated the answers below
Here's a JavaScript solution. Sorry I have no idea how this converts to Factor. reduce is an iterative process
const worker = txt=>
txt.split('').reduce(({floor, basement}, x, i)=> {
if (x === '(')
return {floor: floor + 1, basement}
else if (basement === null && floor === 0)
return {floor: floor - 1, basement: i}
else
return {floor: floor - 1, basement}
}, {floor: 0, basement: null})
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
The answer above relies on some some .split and .reduce which may not be present in your language. Here's another solution using Y-combinator and only the substring built-in (which most languages include). This answer also depends on your language having first-class functions.
const U = f=> f (f)
const Y = U (h=> f=> f (x=> h (h) (f) (x)))
const strhead = s=> s.substring(0,1)
const strtail = s=> s.substring(1)
const worker = Y (f=> ({floor, basement})=> i=> txt=> {
// txt is empty string; return answer
if (txt === '')
return {floor, basement}
// first char in txt is '(', increment the floor
else if (strhead (txt) === '(')
return f ({floor: floor + 1, basement}) (i+1) (strtail (txt))
// if basement isn't set and we're on floor 0, we found the basement
else if (basement === null && floor === 0)
return f ({floor: floor - 1, basement: i}) (i+1) (strtail (txt))
// we're already in the basement, go down another floor
else
return f ({floor: floor - 1, basement}) (i+1) (strtail (txt))
}) ({floor: 0, basement: null}) (0)
{
let {floor, basement} = worker('((((())(())(((()))((')
console.log(floor) //=> 6
console.log(basement) //=> null; never reaches basement
}
{
let {floor, basement} = worker(')(((((')
console.log(floor) //=> 4
console.log(basement) //=> 0
}
{
let {floor, basement} = worker('((())))')
console.log(floor) //=> -1
console.log(basement) //=> 6
}
Related
I am trying to create a code which identifies if the elements in an array are monotonic or not.
I wrote the below code and got the error -
function isMonotonic(array)
if length(array) <= 2
return true
end
check_up = []
check_down = []
for i in range(2, length(array))
if array[i] <= array[i-1]
append!(check_up, 1)
end
if array[i] >= array[i - 1]
append!(check_down, 1)
end
end
if sum(check_up) == length(array) - 1 || sum(check_down) == length(array) - 1
return true
else
return false
end
end
isMonotonic([1, 2, 3, 4, 5, 6 , 7])
I am getting the below error
Error: Methoderror: no method matching zero(::Type{Any})
I think it is because I am trying to sum up the empth array, I want to understand how to overcome this problem in general, I have a solution for the above code, but in genral I want to know the reason and how to use it. I do not want to first check if the array is empty or not and then do the sum.
If you wanted to save yourself lots of effort, the simplest solution would just be:
my_ismonotonic(x) = issorted(x) || issorted(x ; rev=true)
This will return true if x is sorted either forwards, or in reverse, and false otherwise.
We could maybe make it a little more efficient using a check so we only need a single call to issorted.
function my_ismonotonic(x)
length(x) <= 2 && return true
for n = 2:length(x)
if x[n] > x[1]
return issorted(x)
elseif x[n] < x[1]
return issorted(x ; rev=true)
end
end
return true
end
# Alternatively, a neater version using findfirst
function my_ismonotonic(x)
length(x) <= 2 && return true
ii = findfirst(a -> a != x[1], x)
isnothing(ii) && return true # All elements in x are equal
if x[ii] > x[1]
return issorted(x)
else
return issorted(x ; rev=true)
end
end
The loop detects the first occurrence of an element greater than or less than the first element and then calls the appropriate issorted as soon as this occurs. If all elements in the array are equal then the loop runs over the whole array and returns true.
There are a few problems of efficiency in your approach, but the reason you are getting an actual error message is because given the input, either this expression sum(check_up) or this expression sum(check_down) will effectively result in the following call:
sum(Any[])
There is no obvious return value for this since the array could have any type, so instead you get an error. If you had used the following earlier in your function:
check_up = Int[]
check_down = Int[]
then you shouldn't have the same problem, because:
julia> sum(Int[])
0
Note also that append! is usually for appending a vector to a vector. If you just want to add a single element to a vector use push!.
I'm trying to write some code in a functional paradigm for practice. There is one case I'm having some problems wrapping my head around. I am trying to create an array of 5 unique integers from 1, 100. I have been able to solve this without using functional programming:
let uniqueArray = [];
while (uniqueArray.length< 5) {
const newNumber = getRandom1to100();
if (uniqueArray.indexOf(newNumber) < 0) {
uniqueArray.push(newNumber)
}
}
I have access to lodash so I can use that. I was thinking along the lines of:
const uniqueArray = [
getRandom1to100(),
getRandom1to100(),
getRandom1to100(),
getRandom1to100(),
getRandom1to100()
].map((currentVal, index, array) => {
return array.indexOf(currentVal) > -1 ? getRandom1to100 : currentVal;
});
But this obviously wouldn't work because it will always return true because the index is going to be in the array (with more work I could remove that defect) but more importantly it doesn't check for a second time that all values are unique. However, I'm not quite sure how to functionaly mimic a while loop.
Here's an example in OCaml, the key point is that you use accumulators and recursion.
let make () =
Random.self_init ();
let rec make_list prev current max accum =
let number = Random.int 100 in
if current = max then accum
else begin
if number <> prev
then (number + prev) :: make_list number (current + 1) max accum
else accum
end
in
make_list 0 0 5 [] |> Array.of_list
This won't guarantee that the array will be unique, since its only checking by the previous. You could fix that by hiding a hashtable in the closure between make and make_list and doing a constant time lookup.
Here is a stream-based Python approach.
Python's version of a lazy stream is a generator. They can be produced in various ways, including by something which looks like a function definition but uses the key word yield rather than return. For example:
import random
def randNums(a,b):
while True:
yield random.randint(a,b)
Normally generators are used in for-loops but this last generator has an infinite loop hence would hang if you try to iterate over it. Instead, you can use the built-in function next() to get the next item in the string. It is convenient to write a function which works something like Haskell's take:
def take(n,stream):
items = []
for i in range(n):
try:
items.append(next(stream))
except StopIteration:
return items
return items
In Python StopIteration is raised when a generator is exhausted. If this happens before n items, this code just returns however much has been generated, so perhaps I should call it takeAtMost. If you ditch the error-handling then it will crash if there are not enough items -- which maybe you want. In any event, this is used like:
>>> s = randNums(1,10)
>>> take(5,s)
[6, 6, 8, 7, 2]
of course, this allows for repeats.
To make things unique (and to do so in a functional way) we can write a function which takes a stream as input and returns a stream consisting of unique items as output:
def unique(stream):
def f(s):
items = set()
while True:
try:
x = next(s)
if not x in items:
items.add(x)
yield x
except StopIteration:
raise StopIteration
return f(stream)
this creates an stream in a closure that contains a set which can keep track of items that have been seen, only yielding items which are unique. Here I am passing on any StopIteration exception. If the underlying generator has no more elements then there are no more unique elements. I am not 100% sure if I need to explicitly pass on the exception -- (it might happen automatically) but it seems clean to do so.
Used like this:
>>> take(5,unique(randNums(1,10)))
[7, 2, 5, 1, 6]
take(10,unique(randNums(1,10))) will yield a random permutation of 1-10. take(11,unique(randNums(1,10))) will never terminate.
This is a very good question. It's actually quite common. It's even sometimes asked as an interview question.
Here's my solution to generating 5 integers from 0 to 100.
let rec take lst n =
if n = 0 then []
else
match lst with
| [] -> []
| x :: xs -> x :: take xs (n-1)
let shuffle d =
let nd = List.map (fun c -> (Random.bits (), c)) d in
let sond = List.sort compare nd in
List.map snd sond
let rec range a b =
if a >= b then []
else a :: range (a+1) b;;
let _ =
print_endline
(String.concat "\t" ("5 random integers:" :: List.map string_of_int (take (shuffle (range 0 101)) 5)))
How's this:
const addUnique = (ar) => {
const el = getRandom1to100();
return ar.includes(el) ? ar : ar.concat([el])
}
const uniqueArray = (numberOfElements, baseArray) => {
if (numberOfElements < baseArray.length) throw 'invalid input'
return baseArray.length === numberOfElements ? baseArray : uniqueArray(numberOfElements, addUnique(baseArray))
}
const myArray = uniqueArray(5, [])
My teacher just asked this question in the exam and I have no idea where to go on.
More details, the prototype of function is given as:
stack<int> Fibonacci_sequence(int n); //fibonacci numbers count up to n
The point is this function is recursive and it should return a stack data type. In my opinion I don't think this is a possible thing to do, but my teacher asked it!!
P.s: sorry, my language is C++
function stack<int> Fibonacci_sequence(int n) {
if n == 0 {
var a stack<int>;
a.push(0);
return a
} else if n == 1 {
var a stack<int>;
a.push(0);
a.push(1);
return a
} else
var temp int;
var seq int;
seq = Fibonacci_sequence(n-1);
temp = seq.pop;
seq.push(temp);
seq.push(temp);
//above: the top element of the stack must be duplicated because it
//is popped off in the process of calculating the sum.
seq.push(seq.pop()+Fibonacci_sequence(n-2).pop());
return seq
}
}
Above is a function that does just that, written in pseudo code because you did not specify a language. Hopefully this helps, it was fun to come up with! Thanks for the interesting question.
Since you didn't specify a language, and you specified it's an exam, here it is in Ruby. Ruby provides stack operations for arrays, but I'm only using push and pop operations in the following so you should be able to easily translate it to the language of your choice.
def fib(n) # no explicit return type, since everything's an object in Ruby
fail "negative argument not allowed" if n < 0
if n > 1
stack = fib(n - 1)
# grab the last two values...
f_n_1 = stack.pop
f_n_2 = stack.pop
# ...and use them to calculate the next.
# The value of this expression is the resulting stack, return it
return stack.push(f_n_2).push(f_n_1).push(f_n_1 + f_n_2)
elsif n == 1
return fib(0).push(1)
else
return [].push(0)
end
end
p fib(10) # => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
You may have to translate this to the language of your exam, but that's appropriate.
Here is my C++ code based on #Elliot pseudo, and it got errors, I specified these errors in the code. And I just figure out that pop() doesn't return a value, I'm gonna fix this.
stack<int> Fibonacci_sequence(int n)
{
if (n == 0) {
stack<int> a;
a.push(0);
return a;
}
else if (n == 1) {
stack<int> a;
a.push(0);
a.push(1);
return a;
}
else
{
int temp;
temp = Fibonacci_sequence(n - 1).pop(); //error C2440: '=': cannot convert from 'void' to 'int'
Fibonacci_sequence(n - 1).push(temp);
Fibonacci_sequence(n - 1).push(temp);
//above: the top element of the stack must be duplicated because it
//is popped off in the process of calculating the sum.
return Fibonacci_sequence(n - 1).push(Fibonacci_sequence(n - 1).pop() + Fibonacci_sequence(n - 2).pop());//error C2186: '+': illegal operand of type 'void'
}
}
Let's say I have buffer=Int[1,2,3,2,3] and token=[2,3].
Is there any preferred way of searching the occurrence of token in buffer to find [2,4] as the answer.
Or, perhaps, is there any split equivalent function for the integer arrays in julia?
(I know how I can perform this operation using 2 nested loops. However, I am especially interested if there is a more Julian way of doing this.)
Because Julia doesn't have conditionals in list comprehensions, I would personally use filter(). Thus if arr = Int64[1,2,3,4,5,2,3,6,2,3,3,2,2]:
filter(x -> arr[x] == 2 && arr[x + 1] == 3, 1 : length(arr) - 1)
=> [2,6,9]
To make it a little more reusable:
pat = [2,3]
filter(x -> arr[x : x + length(pat) - 1] == pat, 1 : length(arr) - length(pat) + 1)
=> [2,6,9]
Julia does have built-ins like find([fun], A), but there's no way that I'm aware of to use them to return indexes of an ordered sublist.
Of course it's arguably more legible to just
ndxs = Int64[]
for i = 1:length(arr)-1
if arr[i] == 2 && arr[i+1] == 3
push!(ndxs, i)
end
end
=> [2,6,9]
For practice I have also made trial-and-errors and the following patterns have worked for Julia0.4.0. With A = Int[1,2,3,2,3] and pat = Int[2,3], the first one is
x = Int[ A[i:i+1] == pat ? i : 0 for i=1:length(A)-1 ]
x[ x .> 0 ] # => [2,4]
the second one is
x = Int[]
[ A[i:i+1] == pat ? push!(x,i) : 0 for i=1:length(A)-1 ]
#show x # => [2,4]
and the third one is
find( [ A[i:i+1] == pat for i=1:length(A)-1 ] ) # => [2,4]
(where find() returns the index array of true elements). But personally, I feel these patterns are more like python than julia way...
This question is related to this previous thread.
I followed Tomas's suggestion using this piece code, and all works fine:
let GetSameColorNeighs (grid:Option<Ball>[,], row, col, color:Color) =
let rec loop (row, col) = seq {
if not (row < 0 || col < 0 || row > MaxLineNumber - 1
|| col > BallsPerLine - 1) then
let ball = grid.[row,col]
match ball with
| Some(ball) ->
if (!ball.visited = false || not <| ball.color.Equals(color)) then
// Not sure what you want here - yield items using 'yield'?
// [row , col]
else
ball.visited := true
yield row, col // Add single item to results
yield! loop(row + 1, col + 1) // Add all generated to results
yield! loop(row - 1, col - 1) // -- || --
| None -> () }
loop(row, col) |> Seq.toList
The code above iterate through an array 2d of "balls" and return a list of index of adjacent balls with the same color.
Now I have to modify the function in way that it returns also a boolean indicating if at least one ball of the list satisfy a certain condition. I changed the code this way but seems that I can't assign a mutable value inside that code:
let GetSameColorNeighs (grid:Option<Ball>[,], row, col, color:Color) : List<int * int> * bool =
let mutable b : bool = false
let rec loop (row, col) = seq {
if not (row < 0 || col < 0 || row > MaxLineNumber - 1
|| col > BallsPerLine - 1) then
let ball = grid.[row,col]
match ball with
| Some(ball) ->
if (ball.visited = true || not <| ball.color.Equals(color)) then
()
else
//HERE's THE PROBLEM
if (ball_satisfy_a_certain_condition) then
b <- true
ball.visited := true
yield row, col // Add single item to results
yield! loop(row + 1, col + 1) // Add all generated to results
yield! loop(row - 1, col - 1) // -- || --
| None -> () }
loop(row, col) |> Seq.toList, b
It seems that a mutable variable can't be acquired by a closure (I don't know what it means).
So I have 2 questions:
why is the above assignment to a mutable variable wrong?
How should I refactor my code to achieve this goal?
In short, you have to use ref variables instead of mutable variables.
While mutable variables are allocated on the stack, ref variables are heap-based. After each time your loop function is invoked, mutable values are wiped out when ref values are still there. Therefore, only ref values are valid to return in GetSameColorNeighs.
This question has been asked many times here. See The mutable variable 'i' is used in an invalid way.? and this blog post for more in-depth discussion.