1

I'm trying to find an algorithm on writing a high-level function - that doesn't have to halt - in Python (or Pseudo Code) that for a given list of functions [f1,...,fn] and a given list of inputs [x1,...,xn] prints fi(xj) (meaning all the pairs of functions and inputs) only once.

Of course the naive suggestion of implementing this would be:

for f in lst1:
   for x in lst2:
      f(x)

But the catch is that some of functions may loop forever on some inputs.

I am allowed to use a stepper (which has a step() method that makes one subsequent computation step of the function each time called)

I just need an idea or an algorithm on how to do so.

Kyle
  • 154
  • 1
  • 2
  • 13

1 Answers1

7

The general concept for this is dovetailing. Instead of trying to run each alternative to completion, you interleave the various alternative. For example, given three computations c1, c2, c3:

  • Run step 1 of c1
  • Run step 1 of c2
  • Run step 1 of c3
  • Run step 2 of c1
  • Run step 2 of c2, it terminates
  • Run step 2 of c3
  • Run step 3 of c1
  • Run step 3 of c3
  • ...

If a computation halts, this procedure reaches its halting state in finite time, regardless of how many other computations don't halt. Running k computations this way only slow each computation down by a factor of k. Since this is probably homework, I won't spell out how to apply this to your specific problem.

Of course, this does not halt if any of the computations doesn't halt. This is unavoidable (cf. halting problem) and not a problem since you'll finish with those that do halt (in your case: output their results) before you enter an futile infinite loop.

  • This is what the link I posted does via generators and a simple queue based scheduler. – aruisdante May 06 '14 at 16:19
  • Though obviously this solution does require that each step is halting. – aruisdante May 06 '14 at 16:21
  • @aruisdante This is a problem when you work with arbitrary generators, but not when you have computations split in discrete, short steps that you can perform at will. Or when you do preemptive scheduling :-) –  May 06 '14 at 16:22
  • Sure, but assuming you're working inside the constraints of python you can't preempt its call stack externally (as far as I know) :p The correct solution of course would be multi-threading, but I assume that's not the point of the assignment, which we are all presuming is to basically build a 'threading' environment inside python. – aruisdante May 06 '14 at 16:23
  • however finite can be very very long ... eg very hard to distinguish between `return sum(itertools.count(1))` and `return [a**b/float(c) for a in xrange(1000000) for b in xrange(10000000) for c in xrange(100000)]` – Joran Beasley May 06 '14 at 16:24
  • @JoranBeasley That goes without saying. On the bright side (as I already wrote) it's at most k times as many steps for k functions. –  May 06 '14 at 16:25
  • @JoranBeasley which is the problem with any cooperative multi-tasking environment ;) – aruisdante May 06 '14 at 16:25
  • 1
    @aruisdante Note that the assignment has a `step` function which presumably fulfills the requirements for this to work. –  May 06 '14 at 16:26
  • Which python already has built in with its ``yield`` keyword (and probably part of why they named it yield, since yield in every other language is used to release the CPU for cooperative multi-tasking). Seriously, read the link I posted in the question comments, it's a really clever way of using generators. – aruisdante May 06 '14 at 16:27
  • @aruisdante I've already read that long ago. I've even written such code myself and explained those concepts to other people on this very platform. It's really quite cool. But as you note, `yield` is not enough for a `step` function (`next`), since the generator can loop indefinitely and never reach a `yield`. –  May 06 '14 at 16:30
  • But it can loop indefinitely and not reach a ``step`` function either, can it not? Unless your ``step`` has a way to preempt the call stack, which I don't think you can in python. In other words if you're building a cooperative multitasking scheduler, your tasks need to, well, cooperate. – aruisdante May 06 '14 at 16:31
  • @aruisdante We're in the theory of computation here, when we say we run a computation for one fixed-length step (often the transition function of the underlying machine model) we goddamn run a computation for one fixed-length step. And no, it's probably not possible to actually reliably implement this in Python (though one could engineer an implementation that allows this), but since `step` is supplied with the assignment we can safely assume it works for the purpose of the assignment. –  May 06 '14 at 16:34
  • @delnan And I totally agree with you if that's what ``step`` does, but I was under the impression that the OP meant ``step`` in the since that the task-functions can call it to signal to the scheduler to advance (I.E. ``yield``). If it's instead a pre-empter that the scheduler calls to interrupt the task, then sure, absolutely. Perhaps the OP should clarify. Although note that preemption still assumes that the scheduler is not running in the same thread as the task (or that the task returns at the end of every ``step`` call), otherwise it can't really preempt anything. – aruisdante May 06 '14 at 16:37
  • @aruisdante Again, from a computational perspective there isn't really any problem. You don't have a function and schedule it on a piece of silicon and preempt it. You *perform one computation step* (much like an interpreter), and then you stop doing that to carry on with something else. How to actually implement `step` is neither important (it's not `step` that has to be implemented), nor very interesting (in practice you'd just use real threads and rely on the OS to pre-empty). –  May 06 '14 at 16:42
  • @delnan You seem to be arguing computational theory with me when I've clearly agreed with you on the underlying computational theory (hence why I up-voted). I mean this is multi-task scheduling 101. Actually being helpful for the OP's specific problem does somewhat matter whether it's the scheduler preempting or the tasks yielding, and the specifics of the language choice of implementation. – aruisdante May 06 '14 at 16:44
  • @aruisdante I keep going back to computational theory because I believe OP's assignment is a computational theory assignment, which might not even be actually executed, not a OS 101 (or whatever covers scheduling) assignment. OP says "I am allowed to use a stepper", which I read as "I am supplied a stepper". If OP also needs to implement that, they should say so and I'll be glad to join you discussing scheduling. But right now I don't believe OP's problem is in any way related to any scheduler. –  May 06 '14 at 16:47
  • @delnan yes this is a computational models assignment. The idea you have provided is what I also thought of doing but I just spent quite some time trying to implement it using Python. It just doesn't seem to work. Keep in mind you have multiple functions and multiple inputs for each one. Your idea who have worked perfectly had I been given only one function to compute instead of a list of those. – Kyle May 06 '14 at 16:56
  • @user3608890 The details depend on how `step` works, but note that there are n * m *computations*: For each of the n functions, m computations (one for each of the m inputs). –  May 06 '14 at 16:59