Mechanizm switch-case w języku Smalltalk

Język Smalltalk nie oferuje wraz ze swoimi standardowymi klasami odpowiednika mechanizmu switch-case, popularnego na przykład w językach wywodzących sie z języka C. Dlatego programiście, który chciałby takiego mechanizmu użyć pozostaje stworzenie go sobie samodzielnie. Poniżej został przedstawiony prosty sposób implementacji takiego mechanizmu.

Pierwszym krokiem będzie stworzenie klasy Switch jako podklasy klasy Object i zdefiniowanie trzech zmiennych: value, satisfied i response.

Object subclass: #Switch
    instanceVariableNames: 'value satisfied response'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Objectspace.net-Controlling Structures'

Zmienna value będzie przechowywała obiekt, dla którego wykonywany jest switch, zmienna satisfied informuje nas, czy któryś z warunków case został już spełniony, a zmienna response będzie przechowywała wynik operacji, która zostanie wykonana w wypadku spełnienia któregoś z warunków case.

Następnie zdefiniujemy metodę klasową for: tworzącą instancję klasy Switch.

Switch>>for: anObject
   ^self new value: anObject 

Aby uniknąć konieczności inicjalizacji zmiennej satisfied wartością false zdefiniujemy metodę isSatisfied, która zwróci true tylko w wypadku, gdy zmienna satisfied ma wartość true.

Switch>>isSatisfied
   ^satisfied == true 

Teraz pozostaje nam stworzyć metody case:then: i default:.

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

   self isSatisfied ifFalse:[
      (oneArgTestBlock value: self value) ifTrue:[
         self response: execBlock value.
         self satisfied: true]].
   ^self response

Switch>>default: execBlock
   self isSatisfied ifFalse: [
	self response: execBlock value].
   ^self response 

I już możemy pisać:

(Switch for: x)
   case: [:value | value = 1] then: ["..."];
   case: [:value | value = 2] then: ["..."];
   default: ["..."]  

Dla tak prostego bloku testującego jak powyżej możemy stworzyć bardzo przydatną metodę w klasie Switch.

Switch>>caseIs: testObject then: execBlock
   ^self
	case: [:value | testObject = value]
	then: execBlock.  

Wtedy nasz poprzedni przykład będzie wyglądał następująco:

(Switch for: x)
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]  

Powyższą metodę można także zdefiniować w wersji używającej do porównania metody ==, do szybszego porównywania symboli czy klas:

Switch>>caseIdentityIs: testObject then: execBlock
   ^self
	case: [:value | testObject == value]
	then: execBlock.  

Możemy też uprościć sobie przeszukiwanie kolekcji.

Switch>>caseIsAny: testCollection then: execBlock
   ^self
	case: [:value | testCollection includes: value] 
	then: execBlock. 

Wszystko to ładnie wygląda, ale może ktoś chciałby to jeszcze bardziej uprościć i zamiast pisać Switch for: aValue móc po prostu napisać aValue switch. Nic prostszego. W tym celu wystarczy dodać do klasy Object metodę switch.

Object>>switch
   ^Switch for: self 

I już można nasz poprzedni przykład zapisać następująco:

x switch
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]  

I to już nawet przypomina składnię switch-case z innych języków programowania. :-)

(pn)

Przedstawioną wyżej metodę switch można też zdefiniować w następujący sposób:

Object>>switch
   ^Switch for: self value 

Możliwa jest wtedy również następująca składnia:

[x\*10] switch
   caseIs: 1 then: ["..."];
   caseIs: 2 then: ["..."];
   default: ["..."]  

Powyższa implementacja metody umożliwia również proste wykorzystanie instancji klasy ValueHolder w strukturze switch-case. Metoda switch może teraz zostać wysłana bezpośrednio do takiego obiektu, odbiorcą metod typu case będzie obiekt przechowywany przez instancję klasy ValueHolder.

W przedstawionym powyżej mechaniźmie switch-case można również zrezygnować z wykorzystania zmiennej satisfied. Możliwe będzie wtedy wykonanie kilku bloków spełniających warunek case, tak jak ma to miejsce w języku C.

(wh)

Przemysław Nieściór & Wacław Hołub, 02.06.2002


Sources:

or on GitHub:

What's new

  • Deploying Pier on Ubuntu with Plesk at the german provider 1blu
    28 February 201312:21:04 pm by Przemysław Nieściór
    This tutorial describes how to deploy Pier on an Ubuntu Linux distribution with Plesk at the german provider 1blu. It assumes that you want to install a pier image on an Ubuntu Linux with Apache 2 und...
  • Mechanizm switch-case w Smalltalku
    11 June 201212:00:43 pm by Przemysław Nieściór
    ENGLISH VERSION BELOW POLISH VERSION Język Smalltalk nie oferuje wraz ze swoimi standardowymi klasami odpowiednika mechanizmu switch-case, popularnego na przykład w językach wywodzących sie z języka...