Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Gazpacho
Jun 18, 2004

by Fluffdaddy
Slippery Tilde
In Which a Perfectly Sensible Interface for Co-Inductive Types Is Foolishly Extended to Inductive Types

Adbot
ADBOT LOVES YOU

Opinion Haver
Apr 9, 2007

Jewel posted:

So you're saying the item after the current item is nothing? So why not return None?

What if you're iterating over a list with None in it?

M31
Jun 12, 2012
return StopIteration instead

QuarkJets
Sep 8, 2008

Dren posted:

Python code:
for item in my_iterator:
Catches StopIteration. It's hidden from the user but it still happens.

Yes, that's exactly what I said in my post. I said that the user doesn't need to invoke it by hand, which is true; it's taken care of behind the scenes. And this is fine. Invoking get() and catching StopIteration by hand would be stupid (because there's already a very nice control flow mechanism that does this for you)

QuarkJets fucked around with this message at 12:21 on Jan 18, 2014

Nippashish
Nov 2, 2005

Let me see you dance!

M31 posted:

return StopIteration instead

What if you're iterating over a list with StopIteration in it? I don't think there's anything you can return from a function that you can't also put in a list so I don't see how this pattern is going to work.

Edison was a dick
Apr 3, 2010

direct current :roboluv: only

Nippashish posted:

What if you're iterating over a list with StopIteration in it? I don't think there's anything you can return from a function that you can't also put in a list so I don't see how this pattern is going to work.

Return two values, one fo which is false when you finish iterating, or rather than having a .next() method, have a .get() method for the current entry, .next() returns no value, or returns whether there is a next, and a .finished() method which says whether there is a next value in the iterator.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

Nippashish posted:

What if you're iterating over a list with StopIteration in it? I don't think there's anything you can return from a function that you can't also put in a list so I don't see how this pattern is going to work.

Option types work just fine - either you return Nothing, or you return Just <value>, and it's fine. If you're storing an option type in the list, who cares, you're just going to return either Nothing, or Just <option type> - i.e. Nothing (finished iteration), Just Nothing (a Nothing contained in the list), or Just Just <something>.

Of course if you're using a language with option types you probably have a better mechanism than iteration to express what you're trying to do, so in practice this doesn't come into play too much.

nielsm
Jun 1, 2009



Jabor posted:

Option types work just fine

You know I think a simple way Python could have done it would be to have None represent end of iteration, and a single-element tuple represent a value in the iteration. Tadaa, option type defined, the world is saved.

Zombywuf
Mar 29, 2008

HORATIO HORNBLOWER posted:

Exceptions are great, quit being afraid of them and quit pretending anyone gives half a poo poo that they're "expensive." This isn't 1995 and unless you're doing real-time work it literally does not matter at all.

*throws exceptions in inner loop*

"Why is our web server cluster CPU bottlenecked?"

Yeah, this is a thing I've had to fix before. The code in question was filling in an option list for a drop down. IIRC the number of exceptions per second was in the 100,000s.

MononcQc
May 29, 2007

My opinion on exceptions straight outta YOSPOS:

MononcQc posted:

What if you have different classes of exception handling mechanisms available for each of these cases? What about languages that may support exceptions, option types, tagged values, multiple return values, signals, continuations and/or whatever mix of them that exists?

I understand it's very well possible to have a favorite one (say option types), and to pretend that you have to pick only this one in ideal cases, but when faced in a language that offers more than one way to do it, can you still affirm only one of them is the one true form to be used for all cases available?

"One true form of exception handling", to me, sounds as reductionist of an approach as "one true form of concurrency", "one true programming language paradigm", or whatever. This is to say, it's perfectly fine to be opinionated about it and kitchen sink languages might be terrible, but there will be areas where one or more of them is better than some single other.

The general principles for fault tolerance require Separation of Concerns vis. Error Encapsulation (make sure that the contagion doesn't spread), Fault Detection (make sure that you know that someone is infected), and Fault Identification (you have ebola, son).

Error encapsulation (and this applies equally to modules, components, systems, architectures, organizations) is invariably best done at the lowest level possible, which invariably breaks #3 and #4 (fault detection and identification).

Treating all error conditions / exceptions with the same mechanism will generally ensure that you pick similar stances on encapsulation vs. detection and identification for all error conditions / exceptions, unless you decide to be extra careful about all of that.

Using multiple mechanisms will allow you to pick, case by case, which one you feel is worth breaking depending on the nature of the fault and what your specific application or system requires.

Option types are pretty awesome, but they're not blanket replacements for other mechanisms IMO. Context is king.

evensevenone
May 12, 2001
Glass is a solid.

nielsm posted:

You know I think a simple way Python could have done it would be to have None represent end of iteration, and a single-element tuple represent a value in the iteration. Tadaa, option type defined, the world is saved.

That adds a dereference and an if statement to every iteration, plus a bunch of boilerplate.

Exceptions are cheap in Python, especially when caught right away. Maybe they suck in other languages, maybe Haskell has some feature that gets around the need for it, but in python exceptions are a pretty acceptable, and let you write cleaner, clearer interfaces.

comedyblissoption
Mar 15, 2006

Could the 'inappropriate' use of exceptions as control flow be just a symptom of the typically horrible support languages have for explicit multiple return values?

You only really have the following options in most languages:
  • Use a global error flag.
  • Use a special number in your return value. This only works if your special number/value could normally never be returned and requires the user to know the magic number is part of the function interface.
  • Use an input parameter to mean a return value you check.
  • Create a struct/class/tuple just for your particular case.
  • Use an exception for a non-exceptional circumstance.
  • If you're calling a method on an object, provide an IsEnd/whatever method you're supposed to typically call.

These are all just boilerplate and non-explicit ways to really mean you want multiple return values.

comedyblissoption fucked around with this message at 19:08 on Jan 18, 2014

Opinion Haver
Apr 9, 2007

What do you mean by "Create a struct/class/tuple just for your particular case."? Like, in Python you can just return (True, 8) or (False, "you hosed up"). Or is your argument that every function should do this?

comedyblissoption
Mar 15, 2006

Tuples in python are good and are what I mean by decent multiple return values.


Tuples in C# and other statically typed languages have a bit awkward syntax. It's okay-ish I guess besides the syntax.
code:
Tuple<int, bool> Foo()
{
  return new Tuple<int, bool>(0, true);
}

void Bar()
{
  var tuple = Foo();
  var intValue = tuple.Item1;
  var boolValue = tuple.Item2;
}
Unfortunately, Item1 and Item2 don't have semantics regarding what those values mean and requires reading documentation or the source code.

In static languages where you can't have templated tuples, you'd create a class/struct just for returning the tuple you want. This is just a lot of boilerplate.

evensevenone
May 12, 2001
Glass is a solid.

Opinion Haver posted:

Like, in Python you can just return (True, 8) or (False, "you hosed up"). Or is your argument that every function should do this?

There are lots of reasons a function might fail though, and the calling code might need to do different things depending on what happened, so instead of just True or False you probably want to return an object that describes the failure somehow.

Of course, whomever writing the calling code probably has a few code paths for the vast majority of failures, and a couple code paths that are appropriate only certain failures, so it'd probably be nice if you used a class hierarchy so the calling code could write handlers with varying levels of specificity.

And of course, since your function has multiple return points and it might be hard to tell from the error type exactly what was wrong, that object should maybe include a message with more details and a record of which return point was used.

Finally, if that calling function just can't continue, it should probably go ahead and return right there as well, and pass along details of its failure whatever called it.

That seems like a totally reasonable alternative to exceptions, which are really scary.

Dylan16807
May 12, 2010

evensevenone posted:

There are lots of reasons a function might fail though, and the calling code might need to do different things depending on what happened, so instead of just True or False you probably want to return an object that describes the failure somehow.

If something went wrong, throw an exception.

If you hit the end of the list, return (False, None).

It's not that such a mechanism should replace all exceptions (unless you're coding in Go), it's that it would replace many 'bad' uses of exceptions.

Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy
While we're on this exceptions kick, here's some code from a project we inherited from another company:

C# code:
try
{
	var doc = query.First();

	if (doc == null)
	{
		SetError("No Matches Found");
		return;
	}

	// Do some stuff with doc
}
catch (Exception)
{
	SetError("No Matches Found");
	return;
}
For those who don't know .NET, First() throws an exception if no element is found matching the query. Meanwhile, FirstOrDefault() just returns null.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Bognar posted:

While we're on this exceptions kick, here's some code from a project we inherited from another company:

Here's my entry into exception week. I've posted this before, but it bears repeating:

code:
try 
{
    DoSomething();
}
catch (Exception ex)
{
    if (ex.GetType() == typeof(SomeMoreSpecificException)) 
    {
       DoSomeErrorLogging();
    }
    else if ... // repeat for several more exception types
    else 
    {
        throw ex;
    }
}

raminasi
Jan 25, 2005

a last drink with no ice
If we're resubmitting...

C++ code:
int main() {
    try {
        // the entire application
    }
    catch (...) {
        throw 1;
    }
}

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe
This is the worst exception-related horror I've ever seen: doing typechecking with try/catch.

Java code:
void doThing(Object obj) {
  try {
    throw obj;
  } catch(Butt butt) {
    butt.fart();
  } catch(Fart fart) {
    fart.butt();
  }
}
This was from a Verizon internal project.

Bognar
Aug 4, 2011

I am the queen of France
Hot Rope Guy

Ithaqua posted:

Here's my entry into exception week. I've posted this before, but it bears repeating:

Wow, that is... extra special :cripes:. Not only does it check exception type instead of catching specific exceptions, it re-throws the exception so you lose the stack trace. Wonderful.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

Suspicious Dish posted:

This is the worst exception-related horror I've ever seen: doing typechecking with try/catch.

Java code:
void doThing(Object obj) {
  try {
    throw obj;
  } catch(Butt butt) {
    butt.fart();
  } catch(Fart fart) {
    fart.butt();
  }
}
This was from a Verizon internal project.

Yes, this sounds about right for what I've seen of Verizon.

My favorite is:

Java code:
void safeDelete(File f) {
  try {
    f.delete()
  } catch(IOException ex) {
    Log.e("Could not delete " + f);
    doSomething();
  } catch(Exception ex) {
    Log.e("Could not delete " + f);
    doSomething();
  } catch(Throwable ex) {
    Log.e("Could not delete " + f);
    doSomething();
  }
}

How many issues can you spot?

Fun fact: File.delete (edit: in Android) never throws an exception, I double checked.

Volmarias fucked around with this message at 22:46 on Jan 18, 2014

Tesseraction
Apr 5, 2009

Volmarias posted:

Fun fact: File.delete never throws an exception, I double checked.

Uh, yes it does. Specifically it can throw all the ones mentioned here, depending on your program settings.

That is bad code, though.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Volmarias posted:

Yes, this sounds about right for what I've seen of Verizon.

Yep.

Java code:
public class SpecialsSvcInfoDataBlockPrep implements SvcInfoDataBlockDomainInterface{
	private static Logger _logger = Logger.getLogger(SpecialsSvcInfoDataBlockPrep.class.getName());
	private static HashMap<String,String> siteIconMap; 
	private LinkedHashMap<String,ArrayList> siteGroups = new LinkedHashMap<String,ArrayList>();
	private HashMap<String,Integer> elementCount = new HashMap<String,Integer>();
	private HashMap<String,Integer> siteCount = new HashMap<String,Integer>();
	private ArrayList<String> siteElements = new ArrayList<String>();
	private ArrayList<String> cktElements = new ArrayList<String>();
	private ArrayList<String> listOfSites = new ArrayList<String>();
	private ArrayList<ElementAttributeInfo> attributesList = new ArrayList<ElementAttributeInfo>();
	private ArrayList<ElementAttributeInfo> elementAttributes;
	private HashMap<String,String> cktSiteGroupInfo = new HashMap<String,String>();
	private LinkedHashMap<String,String> attributesInfo;
	private HashMap<String,String> iconLabelInfo;
	private LinkedHashMap<String,LinkedHashMap> elementAttributesInfo = new LinkedHashMap<String,LinkedHashMap>();
	private HashMap<String,HashMap> elementIconLabelInfo = new HashMap<String,HashMap>();
	private HashMap<String,String> linkInfoAttributes;
	private HashMap<String,String> endpoint1Attributes;
	private HashMap<String,String> endpoint2Attributes;
	private HashMap<String,String> connectorMiscAttributes;
	private LinkedHashMap<String,String> equipmentMiscAttributes;
	private LinkedHashMap<String,String> siteMiscAttributes;
	private HashMap<String,String> siteIcons = new HashMap<String,String>();
 	private String ringId= "";
	private HashMap<String,String> elementClass = new HashMap<String,String>();
	private HashMap<String,String> iconTag = new HashMap<String,String>();
	private HashMap<String,String> iconLabel = new HashMap<String,String>();
	private XmlDecoder xmlDecoder = new XmlDecoder();
	private HttpServletRequest request = null;
	private String siteCktId = "";
	private boolean bInterSite = false;
	private boolean bLastLink = false;
	private boolean bLastElement = false;
	private boolean bSiteNotAdded = false;
	private boolean bIsDxc = false;
	private StringBuffer svcInfo = null;
	private SessionDataObject sObj = null;
	private String linkType = "";
	private String parseableTID = "";
	private String parseableAID = "";
	private StringBuffer xConnectLabel = null;
	private String prevLinkType = "";
	private String xConnectFrom = "";
	private String xConnectTo = "";
	private String xConnectLevel = "";
	private StringBuffer attributePath = null;
	private StringBuffer connectorAttribute = null;
	private StringBuffer connectorLabel = null;
	private String testTimeStamp = "";
	private String cktId = "";
	private boolean bIsXConnectDXC = false;
	private String siteIcon = "";
	private String ccmReference = "";
	private String altCcmRef = "";
	private String nextLinkType = "";
	private boolean bIsRingRoute = false;
	private boolean bIsCable = false;
	private boolean bMultiXConnectFromRef = false;
	private boolean bMultiXConnectToRef = false;
	private String firstRingRoute = "";
	private String firstCable = "";
	private String rateCode = "";
	private LinkedHashMap<String,String> portDetails ; 
	private ArrayList<LinkedHashMap<String,String>> portDetailsList;
	private HashMap<String,ArrayList<LinkedHashMap<String,String>>> aidInfoMap = new HashMap<String,ArrayList<LinkedHashMap<String,String>>>();
	private ArrayList<HashMap<String,String>> connectorToMap = new ArrayList<HashMap<String,String>>();
	private String speed;
	private boolean bportDetails = false;
	private boolean bvalidateCCM = false;
	private boolean bIsNormalDxc = false;
	private boolean bIsJack = false;
	private ArrayList<String> xConnectFromRefList ;
	private ArrayList<String> xConnectToRefList ;
	private LinkedHashMap<String,String> extraAttributes;
	private LinkedHashMap<String,LinkedHashMap<String,String>> extraAttributesMap = new LinkedHashMap<String,LinkedHashMap<String,String>>();
	private Interpreter interp = null;
	private String INTRA_SITE_LABEL = "Intra-site Wiring";
	private String INTER_SITE_LABEL = "Inter-site Carrier"; 
	private HashMap<String,String> sitesWithLatLong = new HashMap<String,String>();

netcat
Apr 29, 2008

Suspicious Dish posted:

This was from a Verizon internal project.

After having worked in telecom for a few years now, I'm surprised it's even possible to make phone calls.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

Tesseraction posted:

Uh, yes it does. Specifically it can throw all the ones mentioned here, depending on your program settings.

That is bad code, though.

Sorry, I left out relevant info. This is for Android, so in this case it doesn't.

Tesseraction
Apr 5, 2009

Ahhh, Android. Never used the Android API so my bad!

Polio Vax Scene
Apr 5, 2009



You guys are so lucky, you actually have things in your exception blocks :smith:

gonadic io
Feb 16, 2011

>>=

comedyblissoption posted:

Tuples in python are good and are what I mean by decent multiple return values.


Tuples in C# and other statically typed languages have a bit awkward syntax. It's okay-ish I guess besides the syntax.
...
In static languages where you can't have templated tuples, you'd create a class/struct just for returning the tuple you want. This is just a lot of boilerplate.

Please don't confuse statically typed with having bad syntax.

code:
foo :: (Int, Bool)
foo = (0, True)

bar = ...
    where (intValue, boolValue) = foo

Opinion Haver
Apr 9, 2007

Every time someone assumes that all statically typed languages have lovely syntax because they've only looked at C# and Java, Simon Peyton Jones sheds a single tear.

BigRedDot
Mar 6, 2008

Regarding StopIteration: as demonstrated, the efficiency of an if: else: versus a try: except: depends on the expected rate of occurrence of exceptions. For even not-very-large sequences it becomes more attractive to use exceptions rather than an explicit conditional every iteration. Yes, I think it's a little weird too, but I can also understand the pragmatic compromise, especially considering most python programmers will never use it unless they write their own generators. If you want a bullshit handwavy feel-good justification, I'd note that one of the best features of the python iteration protocol is that it transparently accommodates unbounded sequences, endless data streams coming in over the network or continuous sensor measurements. If you start from the premise that whenever you start iterating, it could potentially be unbounded until shown otherwise, then not having another element is an "exceptional" circumstance.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

BigRedDot posted:

Regarding StopIteration: as demonstrated, the efficiency of an if: else: versus a try: except: depends on the expected rate of occurrence of exceptions. For even not-very-large sequences it becomes more attractive to use exceptions rather than an explicit conditional every iteration.
I was mildly curious about how true this was, so I wrote a stupid benchmark to compare throwing StopIteration to returning either None or a one-element tuple with the value:

Python code:
import timeit

def foo(i):
    pass

def range_throw(count):
    i = 0
    def next():
        nonlocal i
        if i >= count:
            raise StopIteration()
        ret = i
        i += 1
        return ret

    try:
        while True:
            foo(next())
    except StopIteration:
        pass

def range_box(count):
    i = 0
    def next():
        nonlocal i
        if i >= count:
            return None
        ret = i
        i += 1
        return (ret,)

    while True:
        value = next()
        if value is None:
            break
        foo(value[0])

for i in range(20):
    throw = timeit.timeit('range_throw({})'.format(i), number=100000, setup="from __main__ import range_throw")
    box = timeit.timeit('range_box({})'.format(i), number=100000, setup="from __main__ import range_box")
    print('{}: {} {} {}'.format(i, throw, box, 'throw' if throw < box else 'box'))
Results on my machine:
code:
$ python3.3 iter.py
0: 0.09478232590481639 0.049011558992788196 box
1: 0.1275848790537566 0.08804874517954886 box
2: 0.16713000484742224 0.12123160599730909 box
3: 0.17925639590248466 0.16214736294932663 box
4: 0.21667551714926958 0.19481477001681924 box
5: 0.23723997408524156 0.22347195004113019 box
6: 0.27520954608917236 0.2586954250000417 box
7: 0.30811286089010537 0.29740366782061756 box
8: 0.32044170005246997 0.3413127949461341 throw
9: 0.3380983481183648 0.3526116090361029 throw
10: 0.3614896049257368 0.4098500278778374 throw
11: 0.4021583560388535 0.41674605407752097 throw
12: 0.4182376980315894 0.45617239410057664 throw
13: 0.44128391798585653 0.49947810312733054 throw
14: 0.4830696531571448 0.526226898888126 throw
15: 0.5001076168846339 0.5593531460035592 throw
16: 0.53740050108172 0.6290849940851331 throw
17: 0.5915194540284574 0.6342180969659239 throw
18: 0.6488920559640974 0.7306330068968236 throw
19: 0.6769056499470025 0.7659882630687207 throw
So yeah, on anything but very short lists throwing an exception is cheaper than boxing the values.

Tesseraction
Apr 5, 2009

Basically my reasoning is that Guido von Rossum is smarter than me so if he chose for something to happen one way it's probably for a good reason.

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Tesseraction posted:

Basically my reasoning is that Guido von Rossum is smarter than me so if he chose for something to happen one way it's probably for a good reason.

He isn't infallible.

What we'd really like is an as-efficient sentinel value that tells the interpreter to jump to a special handler, but doesn't unwind the stack all the way, but we don't have that.

Tesseraction
Apr 5, 2009

I admit I've not looked into it but doesn't D do that?

Also I know Guido isn't infallible, but I'm firmly of the belief that if someone is smarter than you then you can at least trust them to make relatively sensible decisions in the field they're smarter than you in.

Now if Guido disagreed with me about lovely anime plotlines then I'd be more than happy to argue with him.

Tesseraction fucked around with this message at 22:01 on Jan 19, 2014

evensevenone
May 12, 2001
Glass is a solid.
Doesn't it only unwind until it gets to an except: clause that handles it, which is probably only going to be a frame in most of the cases presented and thus not really any different than the frame returning? I'm guessing that the cost is in instantiating the exception object.

Harik
Sep 9, 2001

From the hard streets of Moscow
First dog to touch the stars


Plaster Town Cop

SurgicalOntologist posted:

I forgot to mention that when a new key is encountered, I reassign all the other keys as well.

Funny enough, I just did exactly this and approached it differently. New sensors are append-only, and removal is a human-initiated operation. Removal is 'shift everyone below the item to be removed up a slot' so the relative order is a constant.

I couldn't imagine it being useful to go around replacing batteries and have all your sensors end up in a new and random order.

Maybe I don't understand what you're doing, but resorting the keys whenever you see a new one doesn't sound right.

Edit: Found your more detailed description. What do you do if someone leaves a controller on within radio range that's a lower ID then the ones your subjects have? Playing "Hang on for 30 minutes while we go hunt down a stray controller" doesn't seem like a great plan.

For your specific case, something along the lines of putting every sensor you've seen into a 'available' list, then start by requesting user 1 press a button. Go over the available list, then take whichever one has a button pressed and assign it to user 1. Repeat for user 2, etc. Your staff could even do it as they hand the controllers out, grab one from the pile, hit the button, hand to user 1, etc.

Harik fucked around with this message at 07:55 on Jan 20, 2014

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Plorkyeran posted:

So yeah, on anything but very short lists throwing an exception is cheaper than boxing the values.

These things aren't orthogonal. The Python implementors have made a (defensible) implementation choice to make exceptions relatively more efficient. (More accurately, they have declined to spend time making the non-exceptions path more efficient at the disproportionate expense of the exceptions path.) They have also chosen to make tuples relatively expensive because they don't fit into their immediate representation. Of course, had they chosen to use tuples as the intermediate iteration result, they would have made certain that that kind of tuple was efficiently representable, at least along the critical paths, and the need for efficient exceptions would have diminished.

Meh. It's fair to say that the performance argument against exceptions is at least partly a constructed thing. Most languages/environments could provide faster exceptions that they do; for example, I could probably name half a dozen ways in which we could optimize libUnwind and thus make Unix C++ exceptions faster. But there are limits, and the feature (as seen in most of its use cases) is essentially begging for a biased implementation that exaggerate those limits. That makes it easy to just consistently advise against triggering exceptions in hot loops, which (modulo API impacts causing representational threshold effects, like you're showing) should pretty much be unconditionally valid.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
That is a good point. I don't think you could pull off zero-cost (or even nearly zero) Maybe in anything even vaguely resembling CPython, though, so there'd always be an attainable break-even point, and even if it was at something like 10,000 elements I suspect that for the use cases where the decision actually matters the fixed overhead would win out over small overhead per loop iteration.

Adbot
ADBOT LOVES YOU

SurgicalOntologist
Jun 17, 2004

Harik posted:

Funny enough, I just did exactly this and approached it differently. New sensors are append-only, and removal is a human-initiated operation. Removal is 'shift everyone below the item to be removed up a slot' so the relative order is a constant.

I couldn't imagine it being useful to go around replacing batteries and have all your sensors end up in a new and random order.

Maybe I don't understand what you're doing, but resorting the keys whenever you see a new one doesn't sound right.

Edit: Found your more detailed description. What do you do if someone leaves a controller on within radio range that's a lower ID then the ones your subjects have? Playing "Hang on for 30 minutes while we go hunt down a stray controller" doesn't seem like a great plan.

For your specific case, something along the lines of putting every sensor you've seen into a 'available' list, then start by requesting user 1 press a button. Go over the available list, then take whichever one has a button pressed and assign it to user 1. Repeat for user 2, etc. Your staff could even do it as they hand the controllers out, grab one from the pile, hit the button, hand to user 1, etc.

Not sure if that's what you meant, but our sensors don't have buttons. If the batteries all get replaced the sensors end up in the same order, since they're sorted by physical device number.

The closest solution to what you propose would be to have the experimenter type the device numbers of the sensors in use, in whatever order they choose. I can't say it's an obvious choice either way but I went for convenience over the chance of a missing sensor screwing things up. Anyways, battery life is bad enough that if someone doesn't put the sensor back on the rack to charge they've already screwed up.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply