全 185 件のコメント

[–]lambdaq 6ポイント7ポイント  (4子コメント)

IMHO Ruby's case...when syntax is a work of art. You can have regex and range match ups.

[–]Jazztoken 5ポイント6ポイント  (1子コメント)

With a name like lambda q, I'd think you'd see that as child's play. Give me functional pattern matching or give me death!

[–]iconoclaus 0ポイント1ポイント  (0子コメント)

see Elixir if you like Rubyesque syntax but need a functional approach.

[–]Freeky 3ポイント4ポイント  (0子コメント)

For those unfamiliar:

case foo
when "foo", "bar"
when SomeClass
when 0..42
when /somepattern/
end

Boils down to syntax sugar for:

if "foo" === foo || "bar" === foo
elsif SomeClass === foo
elsif 0..42 === foo
elsif /somepattern/ === foo
end

So it's just calling the === method on each matching object and letting it pattern match as appropriate - equality check, class ancestory check, range inclusion check, regexp match, etc.

[–]Godd2 0ポイント1ポイント  (0子コメント)

It will also call procs for you, passing in the object of interest. If the proc returns something truthy, it's considered a match. Just be careful of side effects!

n = 3

case n
when :even?.to_proc
  puts "It's an even number!"
when :odd?.to_proc
  puts "It was a dumb odd number..."
end

It was a dumb odd number...

To be clear, it still calls #=== on the proc, it's just that the case equality method calls the proc.

a = proc {3}
a === 4
=> 3
a === "hello"
=> 3

[–]FearlessFreep 5ポイント6ポイント  (0子コメント)

I spent years doing Smalltalk, which also doesn't have a switch/case and I think I'd been programming in Python for over a year before I even realized it didn't have a switch/case.

The pseudo-Smalltalk perspective (which tends to be a pure-OO perspective) is partially that methods should be small and switch/case statements tend not to be small structures. But beyond that, usually a switch/case is seen as a bad design "If I'm over here and testing that object to make a bunch of decisions, then the decision probably really doesn't belong here but should be someplace else....either in that object or some in between Visitor, etc.." This tends to work well in Smalltalk for a number of reasons

I now program mostly Python I still more or less follow this philosophy. When I break it I usually just use a dictionary where the keys are the possible test conditions and the values are some sort of executable (lambda or function call)

[–]skulgnome 17ポイント18ポイント  (2子コメント)

Because it doesn't have static typing, and so wouldn't stand to gain from compiling it to a table lookup or binary search.

[–]combinatorylogic 4ポイント5ポイント  (1子コメント)

It's not an issue if the case variants are literals.

[–]dangerbird2 4ポイント5ポイント  (0子コメント)

There are basically zero control structures in Python requiring a literal input. Differentiating compile-time literals and runtime objects doesn't make sense in python, as all values are objects, and all objects are treated as first-class citizens.

[–]bigfig 7ポイント8ポイント  (4子コメント)

So it's more difficult, and what doesn't kill you makes you stronger.

[–]combinatorylogic 27ポイント28ポイント  (1子コメント)

Exactly. The next topic: "How coding in Brainfuck made me a better programmer".

[–]Randosity42 -1ポイント0ポイント  (1子コメント)

What's more difficult about it? A dictionary of functions works just as well, and has the benefit of being an object.

[–]bigfig 0ポイント1ポイント  (0子コメント)

I dunno, I was summarizing the opinion piece as I understood it. Otherwise I have no opinion.

[–]remember_the_aylmao 9ポイント10ポイント  (12子コメント)

class Switch(object):
    def __call__(self, value):
        if not self.verbose:
            if self.switched_value == value:
                if self.fall_through:
                    self.default = True
                    self.cont = True

                else:
                    self.default = False
                    self.cont = False

                return True

            elif self.cont:
                return True

            else:
                return False

        else:
            return bool(value)

    def __init__(self, value, verbose = False, fall_through = True):
        self.switched_value = value
        self.cont = False
        self.default = True
        self.verbose = verbose
        self.fall_through = fall_through

    def fall(self):
        self.default = True
        self.cont = True

    def stop(self):
        self.default = False
        self.cont = False

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

Here's how you use it:

from switch_case import Switch as case

with case(n) as when:
    if when(0):
        print 'You typed zero'
        when.stop()
    if when(1): pass
    if when(9):
        print 'n is a perfect square'
        when.stop()
    if when(2): pass
    if when(4): pass
    if when(6): pass
    if when(8):
        print 'n is an even number'
        when.stop()
    if when(2): pass
    if when(3): pass
    if when(5): pass
    if when(7):
        print 'n is a prime number'
        when.stop()
    if when.default:
        print 'Only single-digit numbers are allowed'

[–]tdz9 15ポイント16ポイント  (2子コメント)

WUT My eyes!!! My eyesssss!!!

[–]remember_the_aylmao 0ポイント1ポイント  (0子コメント)

It could be worse. I could have modified the AST like they do in macropy

[–]vattenpuss 1ポイント2ポイント  (0子コメント)

Smalltalk also has no switch statement. But it's easily added, as in this suggested solution from c2:

Object
    subclass: #Case
    instVars: 'criterion satisfied response'

Case>>for: anObject
    ^self new criterion: anObject

Object>>switch
    ^Case for: self

Case>>case: oneArgTestBlock then: execBlock
    "The oneArgTestBlock must return a Boolean value when passed the criterion of the receiver."

    satisfied ifFalse:
        [(oneArgTestBlock value: criterion) ifTrue:
            [response := execBlock value.
            satisfied := true]].

Case>>default: execBlock
    satisfied ifFalse: [response := execBlock value].
    ^response

And then we can use it, as in your example:

n switch
    case: [:x | x = 0] then: ['You typed zero' print];
    case: [:x | x = 9] then: ['n is a perfect square' print];
    case: [:x | x = 8] then: ['n is an even number' print];
    case: [:x | x = 7] then: ['n is a prime number' print];
    default: ['Only single-digit numbers are allowed' print].

[–]amertune 2ポイント3ポイント  (4子コメント)

More common would be something like this:

from collections import defaultdict
cases = defaultdict(lambda: 'Only single-digit numbers are allowed')
cases.update({
  0: lambda: print('You typed zero'),
  1: lambda: None,
  2: lambda: None,
  3: lambda: None,
  4: lambda: None,
  5: lambda: None,
  6: lambda: None,
  7: lambda: print('n is a prime number'),
  8: lambda: print('n is an even number'),
  9: lambda: print('n is a perfect square')
})

Which would be used like this (REPL example)

> cases[9]()
n is a perfect square
> cases[6]()
> cases['what is this?']()
Only single-digit numbers are allowed

[–]brombaer3000 1ポイント2ポイント  (2子コメント)

Does this structure with defaultdicts have any advantages over the structure used in the first example of the blog post (i.e. using a normal dict and the dict.get(index, default) method or are they equivalent?

[–]amertune 1ポイント2ポイント  (1子コメント)

They're equivalent. I'd favor dict.get(index, default) if the dict was only used in one place, but if I had multiple dict.get calls with the same default I'd prefer using a defaultdict.

[–]brombaer3000 1ポイント2ポイント  (0子コメント)

Ah, I see what you mean now: The dictionary definition itself is prettier using normal dicts, but with defaultdicts you can safely use the more readable bracket syntax for access (dict[index]()), which is nice if you have to write it more than once.

[–]remember_the_aylmao 0ポイント1ポイント  (0子コメント)

Lamdba/dict syntax that people frequently suggest for python misses the ability to use fall through or complex expressions like case(x - 2 > 3).

[–]DrinkingAndFighting 2ポイント3ポイント  (1子コメント)

Seems performant...

[–]remember_the_aylmao 0ポイント1ポイント  (0子コメント)

I suppose you want to have your cake and eat it too? It's python man. Everything is a PyObject. Embrace the chaos.

[–]notconstructive 1ポイント2ポイント  (0子コメント)

It's easy to write dict based dispatchers in Python to do the same thing.

[–]rabidcow 9ポイント10ポイント  (8子コメント)

You can do this easily enough with a sequence of if... elif... elif... else.

That's disgusting.

There have been some proposals for switch statement syntax, but there is no consensus (yet) on whether and how to do range tests.

Oh, well that makes sense, then. I mean, it's pretty obvious to me that you shouldn't, but I understand the allure.

[–]Deto 8ポイント9ポイント  (7子コメント)

Why's it disgusting? I think that If-"else-if"-else feels more natural to anyone who isn't already used to switch-case from another language. Also, the need for "break" statements to prevent fall-through violates the principle of least surprise (or at least, I've never heard of anyone expecting this behavior before being told that's what will happen). Of course, you could get rid of this for a Python version, but then the only difference between switch/case is using different words than if/elif/else. Sure in other languages, it's more efficient because you can directly jump to the case without evaluating every condition, but it looks like this might not be as feasible in Python.

[–]rabidcow 3ポイント4ポイント  (5子コメント)

Sure in other languages, it's more efficient because you can directly jump to the case without evaluating every condition, but it looks like this might not be as feasible in Python.

I don't know why it wouldn't be feasible; I'd make switch essentially syntactic sugar for a dictionary lookup. But it's not a matter of efficiency, it's that you can consider a single branch without having to check all the others to make sure they don't overlap. Worse case, you might have a duplicate, which is much easier to see and obviously incorrect.

If-ladders also make you repeat the discriminant over and over, which is just pointless noise. Until there's that one branch that checks a different variables and you didn't notice...

[–]Eirenarch 1ポイント2ポイント  (4子コメント)

I think that switch is extremely ugly construct and it is not justified in any language. I do use it because it is already there in C style languages but if I had to put together a language switch certainly wouldn't make it in (better have something else). I understand why it was invented in the ancient days just to have a jump table but not today.

[–]rabidcow 3ポイント4ポイント  (3子コメント)

What do you see as ugly about it? Do you disagree with my point about big sequences of if-else?

[–]el_tacomonkey 2ポイント3ポイント  (1子コメント)

I'll jump in. They're both hideous in some sense because they're the same thing. The problem isn't with the syntax (although it would be easy to say the syntax is ugly too), the problem is that you have to hold more things in your head for a longer period of time to read either a switch ... case or if ... elif ... elif than most other expressions.

def the_answer = 42

does not require you to hold much in your head. It's obvious, you can make the association easily. If .. else isn't bad if the inside expression isn't complicated or is self-documentingly named. You only have to "push one thing on the stack" in your mind to get it. To me, that's a beautiful line of code. Switch statements (and their cousin the if else sequences make you keep a bunch of things in mind at the same time while you're reading through the code. Not only do you have to have all that in mind, you have to remember what execution paths didn't happen. That's harder. Not impossible, not by any means, just harder. And your brain only has the time and bandwidth to deal with so many efforts in one day it's "hideous" to "waste" one of them working through complicated control flows.

I think they're hideous because I would have preferred to spend that "mental compute cycle" on the problem of WHY I'm reading this code.

[–]rabidcow 0ポイント1ポイント  (0子コメント)

the problem is that you have to hold more things in your head for a longer period of time to read either a switch ... case or if ... elif ... elif than most other expressions.

Yes! This is exactly my problem with if-ladders, but I'd argue that a properly designed switch feature would not have this problem. Think something like what C has, but with fall-through either forbidden or explicit.

[–]Eirenarch 0ポイント1ポイント  (0子コメント)

I think the switch syntax is extremely heavy in C-style languages (2 keywords, braces, columns, breaks required, etc.) In fact I cannot think of a construct that is heavier on syntax than the switch statement. In addition the functionality of switch statement is trivially replicated with if/else.

[–]Serializedrequests 0ポイント1ポイント  (0子コメント)

You could do it like Ruby, and not require the break statement. If you combine several cases, you separate them with commas. Voila, shorter and a little more readable than if-elsif-else and way more readable than C.

[–]SilasX 1ポイント2ポイント  (0子コメント)

Because python prefers "there should be one way of doing it"

  • You can do it with elif x == ? Then no need for switch/case.
  • You can do +=1? Then no need for ++.
  • You can do comprehensions? Then no need for map/filter (they exist, but are deprecated).

Per the Zen of Python

There should be one-- and preferably only one --obvious way to do it.

[–]MpVpRb 0ポイント1ポイント  (7子コメント)

I can't imagine writing code without a switch statement

I use finite state machines a lot, and switch seems to work well for them

Also, communication protocols with commands represented in an enumeration..switch seems tailor made to handle these

I have no idea why it was omitted from Python

Maybe the author valued philosophical purity more than usefulness

[–]dangerbird2 3ポイント4ポイント  (0子コメント)

Maybe the author valued philosophical purity more than usefulness

That's pretty much the whole point of Python. It's a language controlled by a Benevolent Dictator for Life with language features designed to ensure readable code with consistent idioms across the entire ecosystem.

[–]Workaphobia 2ポイント3ポイント  (5子コメント)

I have no idea why it was omitted from Python

Maybe the same reason there's no do loop: Because it's not so much more expressive compared to existing alternatives that it warrants making the language more complex.

Yes there are cases like FSMs that map well onto switches (although I don't know why a FSM needs case fall-through as the norm, rather than the exception). But using a dictionary, or methods, is not such a bad idea either. If you can't tolerate the overhead of a method lookup, you're in the wrong language anyway.

[–]MpVpRb 0ポイント1ポイント  (0子コメント)

not so much more expressive

I consider it more readable, and I consider readability to be very important

[–]MsEtheldreda 0ポイント1ポイント  (3子コメント)

That sounds like the same reason the Go developers use for omitting important features that people want.

[–]Workaphobia 1ポイント2ポイント  (2子コメント)

If you put in everything that people want, you end up with C++.

Well, I guess it doesn't have garbage collection (yet), but you know what I mean.

[–]MsEtheldreda -4ポイント-3ポイント  (1子コメント)

That isn't even a reasonable comparison. Switch is literally one of the most basic features ever that basically every sane language supports.

[–]Workaphobia 2ポイント3ポイント  (0子コメント)

Then why not the do loop? Why not goto? Why not C-style for loops? Why not multi-level break/continue? (Admittedly, that last one isn't supported in most C-style languages, but it has been requested.)

The fact that other languages have something is not a reason in and of itself for Python to add it. Particularly when Python already has idioms for dealing with the issue in a reasonable way.

[–]Pangloss_ex_machina -1ポイント0ポイント  (15子コメント)

The only thing that I miss in Python is the lack of a ternary operator.

[–]combinatorylogic 16ポイント17ポイント  (14子コメント)

There is a ternary operator in Python - a conditional expression.

[–]Pangloss_ex_machina 8ポイント9ポイント  (7子コメント)

You're right.

Many years using ? and : in other languages and I forgot about this.

[–]masklinn 5ポイント6ポイント  (6子コメント)

tbf the conditional expression is relatively recent (Python 2.5), before that you had to make do with condition and if_true else if_false

[–]LightShadow 3ポイント4ポイント  (3子コメント)

relatively

"Python 2.5 was released on September 19th 2006."

[–]masklinn 1ポイント2ポイント  (2子コメント)

Keep in mind there's a non-empty population of developers for whom the cutoff is less "when was the feature originally released in" and more "when was a version containing that feature released in RHEL/Debian Stable" or even worse "when was the last version not supporting that feature dropped from support".

[–]amertune 1ポイント2ポイント  (1子コメント)

I had to rewrite part of a script for a RHEL 5 server. The old version used the "x if test() else y" form, and I had to revert to the older "test() and x or y" style. It's less readable, but it works the same.

[–]masklinn 0ポイント1ポイント  (0子コメント)

It's less readable, but it works the same.

Not exactly, it fails in one situation (which IME is relatively rare, but it happens): if x is falsy, you'll always get y.

[–]definitely-lying 1ポイント2ポイント  (1子コメント)

I've also seen {True: val1, False: val2}[condition], but yeah it was these various ternary hacks that made them add it, since many of the ternary hacks don't work in the general case and/or aren't lazy.

[–]ohias 2ポイント3ポイント  (0子コメント)

Actually a thing like that can be simpler than a ternary operator if all you need is just an already computed value:

>>> ("false","true")[2 > 1]
'true'

Or it's love for K/Q/J speaking in me

[–]rabidcow 2ポイント3ポイント  (3子コメント)

Although the syntax is like a little slice of Perl.

[–]skulgnome 0ポイント1ポイント  (1子コメント)

It's not rarely that I see ($a && $b) || $c. Easy to tell which languages inherit from Lisp and which do from C.

[–]PM_ME_YOUR_PAULDRONS 0ポイント1ポイント  (0子コメント)

I've not encountered that pattern before. Doesn't it break if $b is false (or coerced into something falsely in languages which do that)? If $b is falsely and $c is true you'll get $c instead even if $a is true won't you?

[–]anacrolix -1ポイント0ポイント  (3子コメント)

Use a dict ffs. Don't mangle the language.

[–]ohias 0ポイント1ポイント  (2子コメント)

case Foo:
     do smth
     assign smth
     if (check smth) break;
     do smth more
case Bar:
     if (check smth more) break;
case Baz:
     ...

how would this look with a dict?

[–]Randosity42 0ポイント1ポイント  (1子コメント)

A dictionary of functions. A complex switch statement should be broken into functions anyway.

[–]ohias 0ポイント1ポイント  (0子コメント)

So could you then show me how my example would look like as a dictionary of functions? It's not very complex, only 9 lines.