martedì 4 maggio 2010

Esercizi Strutture Dati - Queue e Deque

Ecco la seconda parte degli esercizi di Strutture Dati (SD) di Laboratorio di Algoritmi e Strutture Dati (LASD) con il professor Salvatore La Torre. Nel seguito di questo articolo ci saranno le strutture dati Queue (Coda) e Deque (Double ended Queue).






Esercizi su Queue

public interface Queue<E>
{
 /**
  * Restituisce il numero di elementi presenti nella coda.
  * @return un int da 0 alla lunghezza max della coda.
  */
 public int size();
 /**
  * Controlla se la coda è vuota.
  * @return true, se la coda è vuota, false altrimenti.
  */
 public boolean isEmpty();
 /**
  * Accede all'elemento sul front della coda.
  * @return l'elemento al front della coda.
  * @throws EmptyQueueException se invocato su una coda vuota.
  */
 public E front() throws EmptyQueueException;
 /**
  * Inserisce un elemento nel rear della coda.
  * @param element l'elemento da inserire.
  */
 public void enqueue (E element);
 /**
  * Rimuove l'elemento sul front della coda.
  * @return l'elemento rimosso.
  * @throws EmptyQueueException se invocato su una coda vuota.
  */
 public E dequeue()throws EmptyQueueException;
}

Implementare l’interfaccia Queue (scrivere la classe ArrayQueue) usando un array di lunghezza fissata 􏰁􏰁 public static final int CAPACITY = 1024;



􏰀 Implementare Queue in modo che la coda piena invece della FullQueueException causa un aumento della taglia della coda

public class ArrayQueue<E> implements Queue<E>
{
 public static final int CAPACITY = 1024;
 private int f,r,capacity,size;
 private E coda[];

 public ArrayQueue()
 {
  this(CAPACITY);
 }

 public ArrayQueue(int cap)
 {
  capacity = cap;
  f=r=size=0;
  coda = (E[])new Object[capacity];
 }

 @Override
 public int size() {
  return size;
 }

 @Override
 public boolean isEmpty() {
  return (f==r);
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  if (isEmpty())
   throw new EmptyQueueException("Coda Vuota!");
 
  return coda[f];
 }

 @Override
 public void enqueue(E element) 
 {
  if (size == capacity-1)
  {
   E nuovaCoda[] = (E[])new Object[capacity*2];
   for(int i=0; i<size; i++)
   {
    nuovaCoda[i]=coda[(f+i)%capacity];
   }
   f=0;
   r=size;
   capacity *=2;
   coda = null;
   coda = nuovaCoda;
   nuovaCoda = null;
  }

  coda[r] = element;
  r = (r+1)%capacity;
  size++;
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  if (isEmpty())
   throw new EmptyQueueException("Coda Vuota!");
 
  E tmp = coda[f];
  coda[f] = null;
  f = (f+1)%capacity;
  size--;
  return tmp;
 }

 public String toString()
 {
  String response = "ArrayQueue Elements:[ ";
  for (int i = 0; i<size; i++)
  {
   response += ((i+1)+"°"+coda[(f+i)% capacity]+" ");
  }
  response+= "] size:"+size()+" capacity: "+capacity;
  return response;
 }

 public E extract(int k) throws  NotEnoughElements
 {
  if (size() < k+1)
   throw new NotEnoughElements("La Coda non contiene abbastanza elementi!");
 
  return coda[(f+k)%capacity];
 }
}

Scrivere un programma che testi la vostra implementazione di Queue invocandone tutti i metodi





public class ArrayQueueTest 
{
 public static void main(String[] args) 
 {
  ArrayQueue<String> coda = new ArrayQueue<String>(2);
  //QueueWithDeque<String> coda = new QueueWithDeque<String>();
  //LinkedQueue<String> coda = new LinkedQueue<String>();

  coda.enqueue("Marco"); 
  System.out.println(coda);

  coda.enqueue("Rosaria"); 
  System.out.println(coda);

  coda.enqueue("Francesco"); 
  System.out.println(coda);

  coda.enqueue("Pippo"); 
  System.out.println(coda);

  coda.dequeue();
  System.out.println(coda);
 
  coda.enqueue("Giuseppe");
  System.out.println(coda);
 
  System.out.println("Front: "+coda.front());
  //System.out.println("3° elem dal front: "+coda.extract(3));

  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 
  coda.dequeue();
  System.out.println(coda);
 }
}


Esercizi su Deque

public interface Deque<E> 
{
 /**
  * Restituisce il numero di elementi contenuti.
  * @return un valore intero >= 0
  */
 public int size(); 

 /**
  * Verifica se il deque è vuoto.
  * @return true se è vuoto, false altrimenti.
  */
 public boolean isEmpty(); 

 /**
  * Restituisce il primo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E getFirst() throws EmptyDequeException;

 /**
  * Restituisce l'ultimo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E getLast() throws EmptyDequeException;

 /**
  * Aggiunge un nuovo elemento all'inizio del Deque.
  * @param element il nuovo elemento da inserire.
  */
 public void addFirst (E element);

 /**
  * Aggiunge un nuovo elemento alla fine del Deque.
  * @param element il nuovo elemento da inserire.
  */
 public void addLast (E element); 

 /**
  * Rimuove e restituisce il primo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E removeFirst() throws EmptyDequeException;

 /**
  * Rimuove e restituisce l'ultimo elemento del Deque.
  * @throws EmptyDequeException se invocato su Deque vuoto.
  */
 public E removeLast() throws EmptyDequeException; 
}


Per implementare il TDA Deque usiamo una lista doppiamente concatenata




public class DLNode<E>
{
 private E element;
 private DLNode<E> prev, next;

 public DLNode()
 {
  this(null,null,null);
 }
 public DLNode(E e, DLNode<E> p, DLNode<E> n)
 {
  element = e;
  prev = p;
  next = n;
 }

 //metodi accessori
 public E getElement()
 {
  return element;
 }
 public DLNode<E> getPrev()
 {
  return prev;
 }
 public DLNode<E> getNext()
 {
  return next;
 }

 //metodi modificatori
 public void setElement(E e)
 {
  element = e;
 }
 public void setPrev(DLNode<E> p)
 {
  prev = p;
 }
 public void setNext(DLNode<E> n)
 {
  next = n;
 }
}


Implementare il TDA Deque (class MyDeque) con lista doppiamente concatenata

public class MyDeque<E> implements Deque<E>
{

 private int size;
 private DLNode<E> header, trailer;

 public MyDeque()
 {
  header = new DLNode<E>();
  trailer = new DLNode<E>();
  header.setNext(trailer);
  trailer.setPrev(header);
  size = 0;
 }

 @Override
 public int size() 
 {
  return size;
 }

 @Override
 public boolean isEmpty() 
 {
  return (size == 0);
 }

 @Override
 public E getFirst() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto!");
 
  return header.getNext().getElement();
 }

 @Override
 public E getLast() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto!");
 
  return trailer.getPrev().getElement();
 }

 @Override
 public void addFirst(E element) 
 {
  DLNode<E> newNode = new DLNode<E>(element,header,header.getNext());
  header.getNext().setPrev(newNode);
  header.setNext(newNode);
  size++;
 }

 @Override
 public void addLast(E element) 
 {
  DLNode<E> newNode = new DLNode<E>(element,trailer.getPrev(),trailer);
  trailer.getPrev().setNext(newNode);
  trailer.setPrev(newNode);
  size++;
 }

 @Override
 public E removeFirst() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto! - Rimozione primo elem Impossibile");

  E tmp = header.getNext().getElement();
  header.setNext(header.getNext().getNext());
  header.getNext().setPrev(header);
  size--;
  return tmp;
 }

 @Override
 public E removeLast() throws EmptyDequeException 
 {
  if (isEmpty())
   throw new EmptyDequeException("Deck Vuoto! - Rimozione ultimo elem Impossibile");

  E tmp = trailer.getPrev().getElement();
  trailer.setPrev(trailer.getPrev().getPrev());
  trailer.getPrev().setNext(trailer);
  size--;
  return tmp;
 }

 public String toString()
 {
  String risp = "Deque Elements: [ ";
 
  DLNode<E> nodo = header.getNext();
  for (int i=0; i<size; i++ )
  {
   risp+= (i+1)+"°"+nodo.getElement() + " ";
   nodo = nodo.getNext();
  }
  risp+= "] size: "+size;
 
  return risp;
 }
}



Usare Deque per implementare Stack e Queue


public class StackWithDeque<E> implements Stack<E>
{

 private MyDeque<E> contenitore;

 public StackWithDeque()
 {
  contenitore = new MyDeque<E>();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E top() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getLast();
  }
  catch (EmptyDequeException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public E pop() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeLast();
  }
  catch (EmptyDequeException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public void push(E o) throws FullStackException 
 {
  contenitore.addLast(o);
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 public String toString()
 {
  return(contenitore.toString());
 }

}

public class QueueWithDeque<E> implements Queue<E> 
{
 private MyDeque<E> contenitore;

 public QueueWithDeque()
 {
  contenitore = new MyDeque<E>();
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getFirst();
  }
  catch(EmptyDequeException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 @Override
 public void enqueue(E element) 
 {
  contenitore.addLast(element);
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeFirst();
  }
  catch(EmptyDequeException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 public String toString()
 {
  return "Queue with "+contenitore.toString();
 }
}




Implementareilconcettodilistaconcatenatacome classe Java LinkedList



public class Node<E> 
{
 private E element;
 private Node<E> next;

 public Node()
 {
  this(null,null);
 }
 public Node(E e, Node<E> n)
 {
  element = e;
  next = n;
 }

 public E getElement()
 {
  return element;
 }
 public Node<E> getNext()
 {
  return next;
 }

 public void setElement(E e)
 {
  element = e;
 }
 public void setNext(Node<E> n)
 {
  next = n;
 }
}

public class LinkedList<E>
{
 private Node<E> head, tail;
 private int size;


 public LinkedList()
 {
  tail = new Node<E>();
  head = tail;
  size = 0;
 }
 /**
  * Restituisce il numero di elementi contenuti nella lista.
  * @return un valore intero >= 0
  */
 public int size()
 {
  return size;
 }
 /**
  * Verifica se la lista è vuota.
  * @return true se è vuota, false altrimenti.
  */
 public boolean isEmpty()
 {
  return (size == 0);
 }

 /**
  * Aggiunge un nuovo elemento all'inizio della lista.
  * @param e il nuovo elemento da inserire.
  */
 public void addFirst(E e)
 {
  if (size == 0)
   head.setElement(e);
  else
  {
   Node<E> newNode = new Node<E>(e,head);
   head = newNode;  
  }
  size++;
 }
 /**
  * Aggiunge un nuovo elemento alla fine della lista.
  * @param e il nuovo elemento da inserire.
  */
 public void addLast(E e)
 {
  if (size == 0)
   tail.setElement(e);
  else
  {
   Node<E> newNode = new Node<E>(e,null);
   tail.setNext(newNode);
   tail = newNode;  
  }
  size++;
 }

 /**
  * Rimuove e restituisce il primo elemento dalla lista.
  * @throws EmptyLinkedListException se invocato sulla lista vuota.
  */
 public E removeFirst() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException("Lista Vuota!");
 
  E tmp = null;
  if (size == 1)
  {
   tmp = head.getElement();
   head.setElement(null);
  }
  else
  {
   tmp = head.getElement();
   head = head.getNext();
  }
  size--;
  return tmp;
 }

 /**
  * Rimuove e restituisce l'ultimo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E removeLast() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException();

  E tmp = null;
  if(size == 1)
  {
   tmp = tail.getElement();
   tail.setElement(null);
   size--;
  }
  else
  {
   Node<E> nodo = head;
   for(int i=0; i<size-1; i++)
   {
    if (nodo.getNext().getNext() == null)
    {
     tmp = nodo.getNext().getElement();
     nodo.setNext(null);
     tail = nodo;
     size--;
    }
    else
    {
     nodo = nodo.getNext();
    }
   } 
  }
 
  return tmp;
 }

 /**
  * Restituisce il primo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E getFirst() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException();

  return head.getElement();
 }
 /**
  * Restituisce l'ultimo elemento della lista.
  * @throws EmptyLinkedListException se invocato su lista vuota.
  */
 public E getLast() throws EmptyLinkedListException
 {
  if (isEmpty())
   throw new EmptyLinkedListException("Lista Vuota!");

  return tail.getElement();
 }

 public String toString()
 {
  String stringa = "LinkedList Elements: [ ";
  Node<E> nodo = head;
  for(int i=0; i<size; i++)
  {
   stringa += nodo.getElement()+" ";
   nodo = nodo.getNext();
  }
  stringa += "] size: "+size;
  return stringa;
 } 
}



ImplementareilTDAStackconlistaconcatenata 
􏰁 Definire classe LinkedStack


public class LinkedStack<E> implements Stack<E>
{
 private LinkedList<E> contenitore;

 public LinkedStack()
 {
  contenitore = new LinkedList<E>();
 }

 @Override
 public boolean isEmpty() 
 {
  return contenitore.isEmpty();
 }

 @Override
 public E top() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public E pop() throws EmptyStackException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyStackException();
  }
  return tmp;
 }

 @Override
 public void push(E o) throws FullStackException 
 {
  contenitore.addLast(o);
 }

 @Override
 public int size() 
 {
  return contenitore.size();
 }

 public String toString()
 {
  return contenitore.toString();
 }

}


ImplementareilTDAQueueconlistaconcatenata 
􏰁 Definire classe LinkedQueue


public class LinkedQueue<E> implements Queue<E> 
{
 private LinkedList<E> contenitore;

 public LinkedQueue()
 {
  contenitore = new LinkedList<E>();
 }

 @Override
 public int size() {
  return contenitore.size();
 }

 @Override
 public boolean isEmpty() {
  return contenitore.isEmpty();
 }

 @Override
 public E front() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.getFirst();
  }
  catch(EmptyLinkedListException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 @Override
 public void enqueue(E element) {
  contenitore.addLast(element);
 }

 @Override
 public E dequeue() throws EmptyQueueException 
 {
  E tmp = null;
  try
  {
   tmp = contenitore.removeFirst();
  }
  catch(EmptyLinkedListException e)
  {
   throw new EmptyQueueException();
  }
  return tmp;
 }

 public String toString()
 {
  return "QUEUE with "+contenitore.toString();
 }

}



􏰀 ImplementareilTDADequeconlisteconcatenate



public class LinkedDeque<E> implements Deque<E>
{
 private LinkedList<E> lista;

 @Override
 public int size() {
  return lista.size();
 }

 @Override
 public boolean isEmpty() {
  return lista.isEmpty();
 }

 @Override
 public E getFirst() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.getFirst();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;
 }

 @Override
 public E getLast() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.getLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp; }

 @Override
 public void addFirst(E element) {
  lista.addFirst(element);
 }

 @Override
 public void addLast(E element) {
  lista.addLast(element);
 }

 @Override
 public E removeFirst() throws EmptyDequeException {
  E tmp = null;
  try
  {
   tmp = lista.removeFirst();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;
 }

 @Override
 public E removeLast() throws EmptyDequeException 
 {
  E tmp = null;
  try
  {
   tmp = lista.removeLast();
  }
  catch (EmptyLinkedListException e)
  {
   throw new EmptyDequeException();
  }
  return tmp;  
 }
}






Continua...

Guida Objective-C in Italiano - Le Eccezioni - Parte 12


Siamo giunti alla dodicesima parte della Guida in Italiano alla programmazione in Objective-C, oggi parleremo della Gestione delle Eccezioni. Vi ricordo che potrete trovare tutti i links agli articoli di questa guida nella barra laterale del sito.

Questo post è una traduzione in italiano della Guida all'Objective C, presente sul sito developer.apple.com a questo link. L'Objective C è il linguaggio di programmazione per creare applicazioni per iPhone. Questo articolo può interessare a tutti gli sviluppatori che vogliono imparare le basi di questo linguaggio che è il fondamento per programmare nuove applicazioni per Mac Os X, iPhone ed iPad.



Gestione delle Eccezioni

Il linguaggio Objective-C ha una sintassi per la gestione delle eccezioni simile a quella dei linguaggi Java and C++. Accoppiata con l'uso di NSException, NSError, o classi personalizzate, puoi aggiungere una robusta gestione degli errori per i tuoi programmi. Questo articolo fornisce un sommario della sintassi delle eccezioni e la loro gestione.

Abilitare la gestione delle Eccezioni

Usando gcc versione 3.3 e successive, Objective-C fornisce un supporto per la gestione delle eccezioni. Per attivare il supporto per queste caratteristiche usa l'interruttore -fobjc-exceptions del GNU Compiler Collection (GCC) versione 3.3 e successivo. (Nota che questo rende l'applicazione eseguibile solo su Mac OS X v10.3 e successivi poiché il supporto di runtime per la gestione delle eccezioni e la sincronizzazione non è presente in versioni precedenti del software).

Gestione Eccezioni

Un'eccezione è una condizione speciale che interrompe il normale flusso d'esecuzione del programma. Ci sono una varietà di motivi per cui un'eccezione potrebbe essere generata dall'hardware così come dal software. Esempi includono errori aritmetici come divisioni per zero, uderflow o overflow, chiamate a istruzioni indefinite, e tentare di accedere ad una collezione di elementi fuori dai suoi limiti.

Il supporto delle eccezioni Objective-C gira attorno a quattro direttive di compilazione: @try, @catch, @throw, e @finally:
  • Il codice che può lanciare potenzialmente un'eccezione è racchiuso in un blocco @try.
  • Un blocco @catch() contiene la gestione logica delle eccezioni per le eccezioni lanciate in un blocco @try. Puoi avere blocchi @catch() multipli per catturare diversi tipi di eccezioni.
  • Un blocco @finally contiene codice che deve essere eseguito se un'eccezione è o non è lanciata.
  • Usi la direttiva @throw per lanciare un'eccezione, che è essenzialmente un'oggetto Objective-C. Tipicamente usi un'oggetto NSException, ma non è richiesto.
L'esempio sotto descrive un semplice algoritmo di gestione delle eccezioni:
Cup *cup = [[Cup alloc] init];

@try {
     [cup fill];
}

 @catch (NSException *exception) {
      NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
 }

 @finally {
      [cup release];
 }

Catturare Tipi Diversi di Eccezioni

Per catturare un'eccezione lanciata in un blocco @try, usa uno o più blocchi @catch() subito dopo il blocco @try. I blocchi @catch() dovrebbero essere ordinati dal più specifico al meno specifico. In questo modo potete adattare il processo di eccezioni come gruppi, come mostrato nel listato 11-1.

Listato 11-1 Un gestore di eccezioni
@try {
      ...
 }

@catch (CustomException *ce) { // 1
      ...
 }

@catch (NSException *ne) { // 2
      // si eseguono le elaborazioni necessarie a questo livello.
      ...
 }

@catch (id ue) {
      ...
 }

@finally { // 3
      // si eseguono le elaborazini necessarie se è occorsa o meno un'eccezione
      ...
}

La lista seguente descrive le linee di codice numerate:
  1. Cattura il tipo di eccezione più specifica.
  2. Cattura un tipo di eccezione più generico.
  3. Esegue ogni elaborazione che deve essere eseguita se un'eccezione è lanciata o meno.

Lanciare Eccezioni

Per lanciare un'eccezione devi istanziare un oggetto con l'informazione appropriata, come il nome dell'eccezione e il motivo per cui è stata lanciata.
NSException *exception = [NSException exceptionWithName:@"HotTeaException"
     reason:@"The tea is too hot" userInfo:nil];

@throw exception;
Importante: In molti ambienti, usare eccezioni è ragionevolmente ordinario. Ad esempio, potresti lanciare un'eccezione per segnalare che una routine potrebbe non eseguirsi normalmente - come quando un file manca o i dati non possono essere analizzati correttamente. Le eccezioni consumano intensivamente risorse in Objective-C. Non dovresti usare eccezioni per controlli generali del flusso, o semplicemente per significare errori. Invece dovresti usare i valori di ritorno di un metodo o di una funzione per indicare che è occorso un errore, e fornire informazioni riguardo il problema in un oggetto errore.

In un blocco @catch(), puoi re-lanciare l'eccezione catturata usando la direttiva @throw senza un argomento. Questo può aiutare a rendere il codice più leggibile.

Non sei limitato a lanciare oggetti NSException. Puoi lanciare qualsiasi oggetto Objective-C come un oggetto eccezione. La classe NSException fornisce i metodi che aiutano l'elaborazione dell'eccezione, ma puoi implementare la tua propria classe se vuoi. Puoi anche creare una sottoclasse di NSEdception per implementare tipi di eccezioni, come le eccezioni di file system o eccezioni di comunicazione.

Fine Parte 12

Finisce qui il dodicesimo articolo di questa Guida dedicato alla gestione delle eccezioni nel linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà brevemente del Threading. 

Segnalatemi eventuali errori, e commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

lunedì 3 maggio 2010

Guida Objective-C in Italiano - Selettori - Parte 11


Oggi parleremo dei Selettori del linguaggio Objective-C; vi ricordo che questa è l'undicesima parte della Guida in Italiano alla programmazione in Objective-C, e che nella barra laterale del sito potrete trovare tutti i links agli articoli di questa guida.

Questo post è una traduzione in italiano della Guida all'Objective C, presente sul sito developer.apple.com a questo link. L'Objective C è il linguaggio di programmazione per creare applicazioni per iPhone. Questo articolo può interessare a tutti gli sviluppatori che vogliono imparare le basi di questo linguaggio che è il fondamento per programmare nuove applicazioni per Mac Os X, iPhone ed iPad.


Selettori

In Objective-C, la parola selettore ha due significati. Può essere usata per riferirsi semplicemente al nome di un metodo quando è usato in un messaggio ad un oggetto nel codice sorgente. Si può anche riferire all'identificatore univoco che rimpiazza il nome quando il codice sorgente è compilato. i selettori compilati sono di tipo SEL. Tutti i metodi con lo stesso nome hanno lo stesso selettore. Puoi usare un selettore per invocare un metodo su un oggetto - questo fornisce le basi per l'implementazione del pattern di progettazione target-azione in Cocoa.

Metodi e Selettori

Per efficienza, i nomi full ASCII non sono usati come selettori di metodo nel codice compilato. Invece, il compilatore scrive ogni nome di metodo in una tabella e poi accoppia il nome con un identificatore univoco che rappresenta il metodo a runtime. Il sistema di runtime si assicura che ogni identificatore sia univoco: non ci sono due selettori che sono uguali, e tutti i metodi con lo stesso nome hanno lo stesso selettore.

SEL e @selector

I selettori compilati sono assegnati a un tipo speciale, SEL, per distinguerli dagli altri dati. I selettori validi non sono mai 0. Devi permettere al sistema di assegnare identificatori SEL ai metodi; è futile assegnarli arbitrariamente.

La direttiva @selector() ti permette di riferirti ai selettori compilati, piuttosto che al completo nome del metodo. Qui, il selettore per setWidth:height: è assegnato alla variabile setWidthHeight:
SEL setWidthHeight;

setWidthHeight = @selector(setWidth:height:);

è più efficiente assegnare valori a variabili SEL a tempo di compilazione con la direttiva @selector(). Comunque, in alcuni casi, potresti aver bisogno di convertire una stringa di caratteri ad un selettore a runtime. Puoi farlo con la funzione NSSelectorFromString:
setWidthHeight = NSSelectorFromString(aBuffer);
Le conversioni sono possibili anche nella direzione opposta. La funzione NSStringFromSelector restituisce un nome di metodo per un selettore:
NSString *method;

method = NSStringFromSelector(setWidthHeight);

Metodi e Selettori

Selettori compilati identificano nomi di metodi, non implementazioni di metodi. Il metodo display per una classe, ad esempio, ha lo stesso selettore di un metodo display definito nelle altre classi. Questo è essenziale per il polimorfismo e il collegamento dinamico; permette di inviare lo stesso messaggio a riceventi appartenenti a classi diverse. Se ci fosse un selettore per ogni implementazione di metodo, un messaggio non sarebbe differente da una chiamata di funzione.

Un metodo di classe e un metodo di istanza con lo stesso nome sono assegnati allo stesso selettore. Comunque, a causa dei loro domini separati, non c'è confusione tra i due. Una classe potrebbe definire un metodo di classe display in aggiunta al metodo d'istanza display.

Valori di Ritorno e Tipi di Argomenti dei Metodi

La routine di messaggistica ha accesso alle implementazioni dei metodi solo attraverso i selettori, quindi li tratta allo stesso modo tutti i metodi con lo stesso selettore. Scopre i tipi restituiti di un metodo, e i tipi di dati dei suoi argomenti, dal selettore. Quindi, eccetto per messaggi inviati a riceventi tipati staticamente, il collegamento dinamico richiede che tutte le implementazioni dei metodi con lo stesso nome debbano avere lo stesso valore di ritorno e tipi di argomenti. (riceventi staticamente tipati sono un eccezione a questa regola, dato che il compilatore può imparare l'implementazione del metodo dal tipo della classe).

Sebbene i metodi di classe con lo stesso nome e i metodi di istanze siano rappresentati dallo stesso selettore, possono avere argomenti diversi e tipi di ritorno diversi.

Variare il Messaggio a Runtime

I metodi performSelector:, performSelector:withObject:, e performSelector:withObject:withObject:, definiti nel protocollo NSObject, prendono gli identificatori SEL come loro argomenti iniziali. Tutti e tre i metodi mappano direttamente nella funzione di messaggistica. Per esempio:
[friend performSelector:@selector(gossipAbout:)

withObject:aNeighbor];

è equivalente a:
[friend gossipAbout:aNeighbor];
Questi metodi rendono possibile variare un messaggio a runtime, come è possibile variare l'oggetto che riceve il messaggio. I nomi di variabili possono essere usati in entrambe le metà di un'espressione di un messaggio:
id helper = getTheReceiver();

SEL request = getTheSelector();

[helper performSelector:request];

In questo esempio, il ricevente (helper) è scelto a runtime ( dalla funzione fittizia getTheReceiver), e il metodo che il ricevente è chiesto di eseguire (request) è anch'esso determinato a runtime (dalla funzione ugualmente fittizia getTheSelector).

Nota: performSelector: e i suoi metodi compagni restituiscono un id. Se il metodo che è eseguito restituisce un tipo diverso, dovrebbe essere fatto un cast al tipo appropriato. (comunque, fare il cast non funziona per tutti i tipi; il metodo dovrebbe restituire un puntatore o un tipo compatibile con un puntatore).

Il Design Pattern Target-Azione

Nel suo trattamento di controlli di interfacce utente, l'Application Kit fa buon uso dell'abilità di variare sia ricevente che messaggio.

Oggetti NSControl sono graficamente dispositivi che possono essere usati per dare istruzioni ad un'applicazione. La maggior parte somigliano a dispositivi di controllo del mondo reale come bottoni, interruttori, campi di testo, menu ecc... Nel software questi dispositivi si trovano tra applicazione e utente. Essi interpretano gli eventi che vengono dai dispositivi hardware come la tastiera ed il mouse e li traducono in istruzioni specifiche per l'applicazione. Per esempio, un pulsante etichettato "Find" tradurrebbe un click del mouse in un'istruzione per l'applicazione per iniziare a cercare qualcosa.

L'Application Kit definisce un modello per creare dispositivi di controllo e di suo, definisce pochi dispositivi immediatamente disponibili. Ad esempio, la classe NSButtonCell definisce un oggetto che puoi assegnare ad un'istanza di NSMatrix ed inizializzare con una taglia, un'etichetta, un'immagine, un carattere, e una tastiera alternativa. Quando l'utente clicca sul pulsante (o usa la tastiera alternativa), l'oggetto NSButtonCell invia un messaggio che istruisce l'applicazione a fare qualcosa. Per fare questo, un oggetto NSButtonCell deve essere inizializzato non solo con un'immagine, dimensione ed etichetta ma con direzioni su che messaggio inviare e a chi inviarlo. Di conseguenza un'istanza di NSButtonCell può essere inizializzata per un messaggio d'azione, il selettore del metodo che dovrebbe usare nel messaggio che invia, ed il target, l'oggetto che dovrebbe ricevere il messaggio.
[myButtonCell setAction:@selector(reapTheWind:)];

[myButtonCell setTarget:anObject];

Il pulsante cella invia il messaggio usando il metodo performSelector:withObject: di NSObject. Tutti i messaggi d'azione prendono un singolo argomento, l'id del dispositivo di controllo che invia il messaggio.

Se Objective-C non permettesse ai messaggi di essere variati, tutti gli oggetti NSButtonCell dovrebbero inviare lo stesso messaggio; il nome del metodo dovrebbe essere congelato nel codice sorgente di NSButtonCell. Invece di implementare semplicemente un meccanismo per traslare le azioni dell'utente in messaggi d'azione, i pulsanti celle e altri controlli dovrebbero vincolare il contenuto del messaggio. Questo renderebbe difficile per qualunque oggetto rispondere a più di un button cell. Ci dovrebbe anche essere un unico target per un pulsante, o l'oggetto target dovrebbe scoprire da quale bottone viene il messaggio e agire di conseguenza.

Ogni volta che riorganizzi l'interfaccia utente, dovresti anche re-implementare il metodo che risponde al messaggio azione. Questa sarebbe una complicazione non necessaria che l'Objective-C evita felicemente.

Evitare Errori nei Messaggi

Se un oggetto riceve un messaggio per eseguire un metodo che non è nel suo repertorio, risulterà un errore. è la stessa sorta di errore di chiamare una funzione non esistente. Ma poiché i messaggi occorrono a runtime, l'errore spesso non è evidente finché il programma non viene eseguito.

è relativamente semplice evitare questi errori quando il selettore del messaggio è costante e la classe dell'oggetto ricevente è conosciuta. Come scrivi i tuoi programmi, puoi accertarti che il ricevente sia in grado di rispondere. Se il ricevente è tipato staticamente, il compilatore esegue questo test per te.

Comunque, se il selettore dl messaggio o la classe del ricevente varia, potrebbe essere necessario post-porre questo test fino a runtime. Il metodo respondsToSelector:, definito nella classe NSObject, determina se un ricevente può rispondere ad un messaggio. Prende il selettore di metodo come un argomento e restituisce se il ricevente ha accesso a metodi che combaciano con il selettore:
if ( [anObject respondsToSelector:@selector(setOrigin::)] )

[anObject setOrigin:0.0 :0.0];

else

fprintf(stderr, "%s can’t be placed\n",

[NSStringFromClass([anObject class]) UTF8String]);

Il test respondsToSelector: è specialmente importante quando si inviano messaggi ad oggetti di cui non hai il controllo al tempo di compilazione. Ad esempio, se scrivi codice che invia un messaggio ad un oggetto rappresentato da una variabile che altri possono impostare, dovresti accertarti che il ricevente implementi un metodo che risponda al messaggio.

Nota: Un oggetto si può anche sistemare per avere i messaggi che riceve, inoltrati ad altri oggetti se esso non può rispondere direttamente ai messaggi da solo. In tal caso, appare che l'oggetto può gestire il messaggio, anche se l'oggetto risponde al messaggio indirettamente assegnandolo ad un altro oggetto.

Fine Parte 11

Finisce qui l'undicesimo articolo di questa Guida dedicato ai Selettori del linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà della Gestione delle Eccezioni.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti.
Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.


Continua...

domenica 2 maggio 2010

Guida Objective-C in Italiano - Comportamenti Statici - Parte 10


Nel decimo capitolo della Guida in Italiano alla programmazione in Objective-C, parleremo approfonditamente della tipatura statica, abilitare comportamenti statici rispetto a quelli che in Objective-C sono dinamici di default.

Questo post è una traduzione in italiano della Guida all'Objective C, presente sul sito developer.apple.com a questo link. L'Objective C è il linguaggio di programmazione per creare applicazioni per iPhone. Questo articolo può interessare a tutti gli sviluppatori che vogliono imparare le basi di questo linguaggio che è il fondamento per programmare nuove applicazioni per Mac Os X, iPhone ed iPad.


Abilitare Comportamenti Statici

Questo capitolo spiega come funziona la tipatura statica e parla di alcune altre caratteristiche dell'Objective-C, incluso metodi per sovrastare temporaneamente il suo dinamismo inerente.

Comportamento Dinamico di Default

Da progettazione, gli oggetti Objective-C sono entità dinamiche. Quante più decisioni possibili su di loro sono spinte dal tempo di compilazione al tempo di esecuzione:
  • La memoria per gli oggetti è allocata dinamicamente a runtime dai metodi di classe che creano nuove istanze.
  • Gli Oggetti sono tipati dinamicamente. Nel codice sorgente (a tempo di compilazione), ogni variabile di un oggetto può essere tipata id, non importa di che classe sia l'oggetto. L'esatta classe di una variabile id (e quindi i suoi metodi particolari e la struttura dati) non è determinata finché il programma non è in esecuzione.
  • Messaggi e metodi sono legati dinamicamente. Una procedura di runtime fà combaciare il selettore di metodo nel messaggio con un'implementazione di metodo che appartiene al ricevente.
Queste caratteristiche danno ai programmi OO una grande flessibilità, ma c'è un prezzo da pagare. In particolare, il compilatore non può controllare i tipi esatti (classi) delle variabili id. Per permettere un miglior controllo di tipo a tempo di compilazione, e per rendere il codice più auto-documentante, Objective-C permette agli oggetti di essere tipati staticamente con un nome di classe piuttosto che la tipatura generica come id. Permette anche di spegnere alcune delle caratteristiche OO per spostare operazioni dal tempo di esecuzione a quello di compilazione.

Nota: I messaggi sono qualcosa di più lento delle chiamate a funzioni, tipicamente incorrono in una quantità insignificante di lavoro aggiuntivo comparati al lavoro attualmente eseguito. Il caso particolarmente raro in cui bypassare il dinamismo dell'Objective-C potrebbe essere permesso, può essere provato usando gli strumenti di analisi come Shark o Instruments.

Tipatura Statica

Se un puntatore ad un nome di classe è usato al posto di un id in una dichiarazione di oggetto,
Rectangle *thisObject;
Il compilatore restringe il valore della variabile dichiarata ad essere o un'istanza della classe nominata nella dichiarazione o un'istanza di una classe che eredita dalla classe nominata. Nell'esempio sopra, ThisObject può essere solo un Rectangle di un qualche tipo.

Tipare staticamente oggetti ha la stesse strutture dati interne degli oggetti dichiarati id. Il tipo non influenza l'oggetto; influenza dolo la quantità di informazione data al compilatore circa l'oggetto e la quantità di informazione disponibile a quelli che leggono il codice sorgente.

La tipatura statica non influenza neanche il modo in cui l'oggetto è trattato a runtime. Gli oggetti staticamente tipati sono allocati dinamicamente dagli stessi metodi di classe che creano le istanze di tipo id. Se Square (quadrato) è una sottoclasse di Rectangle, il seguente codice produrrà ancora un oggetto con tutte le variabili di istanza di uno Square, non solo quelle di un Rectangle:
Rectangle *thisObject = [[Square alloc] init];
I messaggi inviati ad oggetti tipati staticamente sono legati dinamicamente, proprio come per gli oggetti tipati id. Il tipo esatto di un ricevente tipato staticamente è determinato ancora a runtime come una parte del processo di messaggistica. Un messaggio display inviato a thisObject
[thisObject display];
esegue la versione del metodo definita nella classe Square, non in quella della sua superclasse Rectangle.

Dando al compilatore più informazioni su un oggetto, la tipatura statica apre le possibilità che siano queste info siano assenti per oggetti tipati id:
  • In alcune situazioni, permette i controlli di tipo a tempo di compilazione.
  • Può liberare oggetti dalla restrizione che i metodi nominati identicamente devono avere identici valori di ritorno e tipi di argomenti.
  • Permette di usare la struttura operatore punto per accedere direttamente alle variabili d'istanza di un oggetto.
I primi due argomenti sono discussi nella sezione che segue. Il terzo è spiegato nel secondo articolo di questa guida "Definire Classi".

Controlli di Tipo

Con l'informazione aggiuntiva fornita dalla tipatura statica, il compilatore può consegnare un servizio di controllo di tipi migliore in due situazioni:
  1. Quando un messaggio è inviato a un ricevente tipato staticamente, il compilatore può assicurarsi che il ricevente può rispondere. Un warning è emesso se il ricevente non ha accesso al metodo nominato nel messaggio.
  2. Quando un oggetto tipato staticaemente è assegnato a una variabile tipata staticamente, il compilatore si accerta che i tipi siano compatibili. Emette un warning se non lo sono.
Un assegnamento può essere fatto senza warning, se la classe dell'oggetto definito è identica a, o eredita da, la classe della variabile che riceve l'assegnamento. Il seguente esempio illustra questo:
Shape *aShape;

Rectangle *aRect;

aRect = [[Rectangle alloc] init];

aShape = aRect;
Qui aRect può essere assegnato a aShape perchè un Rectangle è un tipo di Shape - la classe Rectangle eredita da Shape. Comunque, se i ruoli delle due variabili sono invertiti e aShape è assegnata a aRect, il compilatore genera un warning; non tutte le forme sono Rettangoli.

Non c'è controllo quando l'espressione su uno dei due lati dell'assegnamento è un id. Un oggetto tipato staticamente può essere liberamente assegnato ad un id, o un id a un oggetto tipato staticamente. Perché i metodi come alloc ed init restituiscono variabili tipate staticamente. Il seguente codice è incline ad errori, ma ciò nonostante si concede:
Rectangle *aRect;

aRect = [[Shape alloc] init];

Tipi di Ritorno e degli Argomenti

In generale, i metodi in classi diverse che hanno lo stesso selettore (lo stesso nome) devono anche condividere gli stessi tipi di ritorno e tipi di argomenti. Questo vincolo è imposto dal compilatore per permettere il collegamento dinamico (dynamic binding). Poiché la classe di un messaggio ricevente, (e quindi dettagli specifici di classe riguardo al metodo che gli è chiesto di eseguire) non può essere conosciuta a tempo di compilazione, il compilatore deve trattare allo stesso modo tutti i metodi con lo stesso nome. Quando prepara le informazioni sul ritorno del metodo e sui tipi degli argomenti per il sistema di runtime, crea solo una descrizione di metodo per ogni selettore di metodo.

Comunque, quando un messaggio è inviato a un oggetto tipato staticamente, la classe del ricevente è conosciuta dal compilatore. Il compilatore ha accesso a informazioni specifiche della classe riguardo ai metodi. Quindi, il messaggio è liberato dalle restrizioni sui suoi valori di ritorno e tipi di argomenti.

Tipatura Statica a una Classe Ereditata

Un'istanza può essere tipata staticamente alla sua propria classe o a qualunque classe da cui eredita. Tutte le istanze, ad esempio, possono essere tipate staticamente come NSObject.

Comunque, il compilatore capisce la classe di un oggetto tipato staticamente solo dal nome di classe nella designazione del tipo, ed effettua il suo controllo di tipi di conseguenza. Tipare un'istanza a una classe ereditata può quindi risultare in discrepanze tra ciò che il compilatore pensa che potrebbe accadere a runtime e cosa realmente succede.

Ad esempio, se tipi staticamente un'istanza di Rectangle come una Shape,
Shape *myRectangle = [[Rectangle alloc] init];
Il compilatore la tratterà come una Shape. Se invii all'oggetto un messaggio per eseguire un metodo di Rectangle,
BOOL solid = [myRectangle isFilled];
Il compilatore si lamenterà. Il metodo isFilled è definito nella classe Rectangle ma non in Shape.

Comunque, se gli invii un messaggio per eseguire un metodo che classe Shape conosce,

[myRectangle display];

Il compilatore non si lamenterà, anche se Rectangle sovrascrive il metodo. A runtime, è eseguita la versione del metodo di Rectangle.

In modo analogo, supponiamo che la classe sorastante dichiara un metodo worry che restituisce un double,
- (double)worry;
e la sottoclasse di mezzo sovrascrive il metodo e dichiara un nuovo tipo di ritorno:
- (int)worry;

Se un'istanza è tipata staticamente alla classe superiore, il compilatore penserà che il suo metodo worry restituisce un double, e se un istanza è tipata alla classe di mezzo, penserà che worry restituisce un intero. Risulteranno ovviamente errori se un'istanza della classe di mezzo è tipata alla classe superiore. Il compilatore informerà il sistema di runtime che un messaggio worry è inviato ad un oggetto che restituisce un double, ma a runtime esso restituisce attualmente un int e genera un errore.

La tipatura statica può liberare metodi nominati in modo identico dalla restrizione che devono avere valori di ritorno e tipi di argomento identici, ma lo può fare così affidabilmente solo se i metodi sono dichiarati in rami differenti della gerarchia delle classi.

Fine Parte 10

Finisce qui il decimo articolo di questa Guida dedicato alla tipatura statica nel linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà dei Selettori.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti. Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...

sabato 1 maggio 2010

Guida Objective-C in Italiano - Parte 9 - Enumerazione Veloce

Enumerare oggetti di una collezione, tramite la caratteristica Enumerazione Veloce; questo sarà l'argomento trattato oggi, in questa nona parte della Guida in Italiano alla programmazione in Objective-C.

Questo post è una traduzione in italiano della Guida all'Objective C, presente sul sito developer.apple.com a questo link. L'Objective C è il linguaggio di programmazione per creare applicazioni per iPhone. Questo articolo può interessare a tutti gli sviluppatori che vogliono imparare le basi di questo linguaggio che è il fondamento per programmare nuove applicazioni per Mac Os X, iPhone ed iPad.



Enumerazione Veloce

L'enumerazione veloce è una caratteristica del linguaggio che ti permette di enumerare in modo sicuro ed efficiente i contenuti di una collezione usando una sintassi efficiente.

La caratteristica for…in

L'enumerazione veloce è una caratteristica del linguaggio che ti permette di enumerare in modo sicuro ed efficiente i contenuti di una collezione usando una sintassi efficiente. La sintassi è definita come segue:
for ( Tipo nuovaVariabile in espressione ) { statements }
o
Tipo nuovaVariabile;

for ( nuovaVariabile in expression ) { statements }

In entrambi i casi, le espressioni raccolgono un oggetto conforme al protocollo NSFastEnumeration. La variabile che itera è impostata a turno ad ogni elemento nell'oggetto restituito, e il codice definito dagli statements è eseguito. La variabile che itera è impostata a nil quando il ciclo finisce esaurendo la sorgente del gruppo di oggetti. Se il ciclo è terminato presto, la variabile che itera è lasciata a puntare all'ultimo elemento dell'iterazione.

Ci sono molti vantaggi dell'uso dell'enumerazione veloce:
  • L'enumerazione è considerevolmente più efficiente rispetto ad usare direttamente NSEnumerator.
  • La sintassi è concisa.
  • L'enumerazione è sicura - l'enumeratore ha una sentinella di mutazione, così che provi a modificare la collezione durante l'enumerazione, verrà emessa un'eccezione.
Dato che la mutazione dell'oggetto durante l'iterazione è vietata, puoi eseguire anche enumerazioni multiple contemporaneamente.

Adottare l'enumerazione veloce

Qualsiasi classe le cui istanze forniscono un accesso ad una collezione di altri oggetti può adottare il protocollo NSFastEnumeration. La collezione di classi Cocoa - NSArray, NSDictionary, e NSSet - adottano questo protocollo, come fà NSEnumerator. Dovrebbe essere ovvio che nei casi di NSArray e NSSet l'enumerazione riguarda i loro contenuti. Per altre classi, la documentazione corrispondente potrebbe chiarire che proprietà è iterata - ad esempio, NSDictionary e la classe NSManageObjectModel del Core Data forniscono supporto per l'enumerazione veloce; NSDictionary numera le sue chiavi, e NSManageObjectModel enumera le sue entità.

Usare l'enumerazione veloce

Il seguente codice d'esempio illustra l'uso dell'enumerazione veloce con oggetti NSArray e NSDictionary.
NSArray *array = [NSArray arrayWithObjects:
     @"One", @"Two", @"Three", @"Four", nil];

 for (NSString *element in array) {
     NSLog(@"element: %@", element);
 }

 NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
     @"quattuor", @"four", @"quinque", @"five", @"sex", @"six", nil];

 NSString *key;
 for (key in dictionary) {
      NSLog(@"English: %@, Latin: %@", key, [dictionary valueForKey:key]);
 }

Puoi anche usare oggetti NSEnumerator con l'enumerazione veloce, come illustrato nel seguente esempio:
NSArray *array = [NSArray arrayWithObjects:
     @"One", @"Two", @"Three", @"Four", nil];

 NSEnumerator *enumerator = [array reverseObjectEnumerator];
 for (NSString *element in enumerator) {
      if ([element isEqualToString:@"Three"]) {
           break;
      }
 }

 NSString *next = [enumerator nextObject];
 // next = "Two"

Per collezioni o enumeratori che hanno un ordine ben definito - come NSArray o istanze di NSEnumerator derivate da un array - l'enumerazione procede in quell'ordine, quindi, per conoscere l'indice dell'elemento della collezione che vi serve, vi basterà contare le iterazioni.
NSArray *array = /* assume this exists */;

 NSUInteger index = 0;

 for (id element in array) {
      NSLog(@"Element at index %u is: %@", index, element); index++;
 }

In altri rispetti, la caratteristica si comporta come un ciclo for standard. Puoi usare break per interrompere l'iterazione; e se vuoi saltare elementi puoi usare uno statement condizionale annidato come mostrato nel seguente esempio:
NSArray *array = /* assumiamo che questo esista */;

 for (id element in array) {
      if (/* qualche test per l'elemento */) {
           // statements che si applicano ai soli elementi che superano il test
      }
 }

Se vuoi saltare il primo elemento e poi processare non più di cinque ulteriori elementi, potresti fare come mostrato in questo esempio:
NSArray *array = /* assumiamo che questo esista */;
NSUInteger index = 0;

 for (id element in array) {
      if (index != 0) {
           NSLog(@"Element at index %u is: %@", index, element);
      }
      if (++index >= 6) {
           break;
      }
 }

Fine Parte 9

Finisce qui il nono articolo di questa Guida dedicato all'Enumerazione Veloce del linguaggio di programmazione Objective-C. Il prossimo post di questa guida, parlerà di come abilitare comportamenti statici; vedremo quindi più in dettaglio come funziona la tipatura statica, e discuteremo di alcune caratteristiche dell'Objective-C come vari modi per sormontare temporaneamente il suo dinamismo.

Segnalatemi eventuali errori, o commentate l'articolo se l'avete trovato utile, anche per incentivarmi a continuare a pubblicare le mie traduzioni. Iscrivetevi ai feed del blog per essere sempre aggiornati automaticamente ogni volta che sono disponibili nuovi contenuti.
Nella barra laterale del blog potete trovare l'elenco di tutti gli articoli di questa guida.
Continua...
Related Posts with Thumbnails