diff --git a/images/posts/2018-01-02-reflection.jpg b/images/posts/2018-01-02-reflection.jpg new file mode 100644 index 0000000..9192e0b Binary files /dev/null and b/images/posts/2018-01-02-reflection.jpg differ diff --git a/posts/adventofcode/2017/25-the-halting-problem.md b/posts/adventofcode/2017/25-the-halting-problem.md new file mode 100644 index 0000000..1a9756e --- /dev/null +++ b/posts/adventofcode/2017/25-the-halting-problem.md @@ -0,0 +1,150 @@ +--- +title: "The Halting Problem — Python — #adventofcode Day 25" +description: "In which Turing and I have a little chat about machines." +slug: day-25 +date: 2018-01-02T20:47:19+00:00 +tags: +- Technology +- Learning +- Advent of Code +- Python +series: aoc2017 +--- + +[Today's challenge](http://adventofcode.com/2017/day/25), takes us back to a bit of computing history: a good old-fashioned [Turing Machine](http://en.wikipedia.org/wiki/Turing_Machine). + +[→ Full code on GitHub](https://github.com/jezcope/aoc2017/blob/master/25-the-halting-problem.py) + +!!! commentary + Today's challenge was a nice bit of nostalgia, taking me back to my university days learning about the theory of computing. Turing Machines are a classic bit of computing theory, and are provably able to compute any value that is possible to compute: a value is computable *if and only if* a Turing Machine can be written that computes it (though in practice anything non-trivial is mind-bendingly hard to write as a TM). + +A bit of a library-fest today, compared to other days! + +```python +from collections import deque, namedtuple +from collections.abc import Iterator +from tqdm import tqdm +import re +import fileinput as fi +``` + +These regular expressions are used to parse the input that defines the transition table for the machine. + +```python +RE_ISTATE = re.compile(r'Begin in state (?P\w+)\.') +RE_RUNTIME = re.compile( + r'Perform a diagnostic checksum after (?P\d+) steps.') +RE_STATETRANS = re.compile( + r"In state (?P\w+):\n" + r" If the current value is (?P\d+):\n" + r" - Write the value (?P\d+)\.\n" + r" - Move one slot to the (?Pleft|right).\n" + r" - Continue with state (?P\w+).\n" + r" If the current value is (?P\d+):\n" + r" - Write the value (?P\d+)\.\n" + r" - Move one slot to the (?Pleft|right).\n" + r" - Continue with state (?P\w+).") +MOVE = {'left': -1, 'right': 1} +``` + +A `namedtuple` to provide some sugar when using a transition rule. + +```python +Rule = namedtuple('Rule', 'write move next_state') +``` + +The `TuringMachine` class does all the work. + +```python +class TuringMachine: + def __init__(self, program=None): + self.tape = deque() + self.transition_table = {} + self.state = None + self.runtime = 0 + self.steps = 0 + self.pos = 0 + self.offset = 0 + + if program is not None: + self.load(program) + + def __str__(self): + return f"Current: {self.state}; steps: {self.steps} of {self.runtime}" +``` + +Some jiggery-pokery to allow us to use `self[pos]` to reference an infinite tape. + +```python + def __getitem__(self, i): + i += self.offset + if i < 0 or i >= len(self.tape): + return 0 + else: + return self.tape[i] + + def __setitem__(self, i, x): + i += self.offset + if i >= 0 and i < len(self.tape): + self.tape[i] = x + elif i == -1: + self.tape.appendleft(x) + self.offset += 1 + elif i == len(self.tape): + self.tape.append(x) + else: + raise IndexError('Tried to set position off end of tape') +``` + +Parse the program and set up the transtion table. + +```python + def load(self, program): + if isinstance(program, Iterator): + program = ''.join(program) + + match = RE_ISTATE.search(program) + self.state = match['state'] + + match = RE_RUNTIME.search(program) + self.runtime = int(match['steps']) + + for match in RE_STATETRANS.finditer(program): + self.transition_table[match['state']] = { + int(match['read0']): Rule(write=int(match['write0']), + move=MOVE[match['move0']], + next_state=match['next0']), + int(match['read1']): Rule(write=int(match['write1']), + move=MOVE[match['move1']], + next_state=match['next1']), + } +``` + +Run the program for the required number of steps (given by `self.runtime`). `tqdm` isn't in the standard library but it should be: it shows a lovely text-mode progress bar as we go. + +```python + def run(self): + for _ in tqdm(range(self.runtime), + desc="Running", unit="steps", unit_scale=True): + read = self[self.pos] + rule = self.transition_table[self.state][read] + self[self.pos] = rule.write + self.pos += rule.move + self.state = rule.next_state +``` + +Calculate the "diagnostic checksum" required for the answer. + +```python + @property + def checksum(self): + return sum(self.tape) +``` + +Aaand GO! + +```python +machine = TuringMachine(fi.input()) +machine.run() +print("Checksum:", machine.checksum) +``` diff --git a/posts/adventofcode/2017/reflections.md b/posts/adventofcode/2017/reflections.md new file mode 100644 index 0000000..4c427c8 --- /dev/null +++ b/posts/adventofcode/2017/reflections.md @@ -0,0 +1,47 @@ +--- +title: "Reflections on #aoc2017" +description: "In which I look back on 25 days of regular coding." +slug: reflections +date: 2018-01-02T21:02:37+00:00 +type: post +tags: +- Technology +- Learning +- Advent of Code +- Reflection +series: aoc2017 +--- + + +{{% figure alt="Trees reflected in a lake" +src="/images/posts/2018-01-02-reflection.jpg" +caption="Trees reflected in a lake" +attr="Joshua Reddekopp on Unsplash" +attrlink="https://unsplash.com/photos/-3uIUqsR-Rw" +class="main-illustration fr" %}} + +It seems like ages ago, but [way back in November](../advent-of-code-2017/) I committed to completing [Advent of Code](https://adventofcode.com). I managed it all, and it was fun! + +All of my code is [available on GitHub](https://jezcope/aoc2017) if you’re interested in seeing what I did, and I managed to get out a blog post for every one with a bit more commentary, which you can see in the series list above. + +## How did I approach it? + +I’ve not really done any serious programming challenges before. I don’t get to write a lot of code at the moment, so all I wanted from AoC was an excuse to do some proper problem-solving. + +I never really intended to take a polyglot approach, though I did think that I might use mainly Python with a bit of Haskell. In the end, though, I used: Python (×12); Haskell (×7); Rust (×4); Go; C++; Ruby; Julia; and Coconut. + +For the most part, my priorities were getting the right answer, followed by writing readable code. I didn’t specifically focus on performance but did try to avoid falling into traps that I knew about. + +## What did I learn? + +I found Python the easiest to get on with: it’s the language I know best and although I can’t always remember exact method names and parameters I know what’s available and where to look to remind myself, as well as most of the common idioms and some performance traps to avoid. Python was therefore the language that let me focus most on solving the problem itself. C++ and Ruby were more challenging, and it was harder to write good idiomatic code but I can still remember quite a lot. + +Haskell I haven’t used since university, and just like back then I really enjoyed working out how to solve problems in a functional style while still being readable and efficient (not always something I achieved...). I learned a lot about core Haskell concepts like monads & functors, and I’m really amazed by the way the Haskell community and ecosystem has grown up in the last decade. + +I also wanted to learn at least one modern, memory-safe compiled language, so I tried both Go and Rust. Both seem like useful languages, but Rust really intrigued me with its conceptual similarities to both Haskell and C++ and its promise of memory safety without a garbage collector. I struggled a lot initially with the “borrow checker” (the component that enforces memory safety at compile time) but eventually started thinking in terms of ownership and lifetimes after which things became easier. The Rust community seems really vibrant and friendly too. + +## What next? + +I really want to keep this up, so I’m going to look out some more programming challenges ([Project Euler](https://projecteuler.net/) looks interesting). It turns out there’s a regular Code Dojo meetup in Leeds, so hopefully I’ll try that out too. I’d like to do more realistic data-science stuff, so I’ll be taking a closer look at stuff like [Kaggle](https://kaggle.com/) too, and figuring out how to do a bit more analysis at work. + +I’m also feeling motivated to find an open source project to contribute to and/or release a project of my own, so we’ll see if that goes anywhere! I’ve always found the advice to “scratch your own itch” difficult to follow because everything I think of myself has already been done better. Most of the projects I use enough to want to contribute to tend to be pretty well developed with big communities and any bugs that might be accessible to me will be picked off and fixed before I have a chance to get started. Maybe it’s time to get over myself and just reimplement something that already exists, just for the fun of it!