Έχουν δημοσιευτεί Κυριακή, 6 Ιουνίου 2010 6:30 μμ από το μέλος PALLADIN

Α programming puzzle

Πριν από λίγες μέρες, ένας φίλος μου έστειλε ένα programming puzzle που του τέθηκε κατά την διάρκεια ενός job interview.

Το σκέφτηκα για λίγο και του έστειλα μια πρόχειρη λύση.
Με το που είδε τον κώδικα που του έστειλα, μου απάντησε ότι αποκλείεται να είχαν κάτι τέτοιο στο μυαλό τους.

Επειδή είμαι περίεργος να δω και άλλες ιδέες για το πρόβλημα, όσοι φίλοι
θέλουν, μπορούν να αφήσουν ένα comment με τις ιδέες τους. 
Σε επόμενο blog post, θα δώσω την λύση που του έστειλα.

Το puzzle:
Δώστε μια εναλλακτική υλοποίηση της παρακάτω class χωρίς να χρησιμοποιηθεί
κανενός είδους conditional logic construct (if, switch, ()?).

public class Thunk<T>
{
	private T value;
	private bool flag = false;
	private Func<T> func;

	public T Value
	{
		get
		{
			if(!flag)
			{				
				value = func();
				flag = true;
			}
			return value;	
		}
	}

	public Thunk(Func<T> func)
	{
		this.func = func;
	}
}

Share


Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια

Σχόλια:

 

Panagiotis Kefalidis έγραψε:

Μαλλον εγώ δεν πιάνω κατι, αλλά όταν λες χωρίς condition; Πως θα δεις εάν το flag έχει τιμή false ή οχι; Εννοώ μέσα σε Lambda statement το !flag πχ ή το flag == false κλπ, δεν παίζει σαν σενάριο;

Γιατί έγραψα κάτι, αλλά με τον παραπάνω τρόπο.

Ιουνίου 6, 2010 11:41 μμ
 

PALLADIN έγραψε:

Nai prepei i lisi na min exei katholou conditional logic.

To desirable effect tis class einai san cache...

Thunk<int> thunk = new Thunk<int>(() => { Console.WriteLine("Side effect"); return 42; });

int value = thunk.Value; // Output: Side effect value = 42

value = thunk.Value; // cached value = 42 without side effects

Diladi i nea ilopoiisi prepei na exei tin idia simperifora (me to pou zito tin timi simbenei to computation kai cacharete gia tis epomenes kliseis) alla xoris conditional logic.

Ιουνίου 7, 2010 12:07 πμ
 

Markos έγραψε:

Εγώ πάλι δεν καταλαβαίνω τι χρειάζεται το flag και ο private delegate func. Έτσι κι αλλιώς δεν υπάρχει default (parameterless) constructor και, αν δεν κάνω λάθος, η παραπάνω κλάση μπορεί να γραφτεί:

public class Thunk<T>

   {

       private T value;

       public T Value

       {

           get

           {

               return value;

           }

       }

       public Thunk(Func<T> func)

       {

           this.value = func();

       }

   }

Ιουνίου 7, 2010 12:08 πμ
 

Panagiotis Kefalidis έγραψε:

Α μάλιστα, τώρα κατάλαβα.. Αν και τώρα νυστάζω οπότε θα το δω αύριο.. Τόσο απλό αλλά τόσο ενδιαφέρον.

Ιουνίου 7, 2010 12:38 πμ
 

darklynx έγραψε:

Το να εκτελέσεις την func στον constructor παρότι δείχνει βολική λύση είναι διαφορετική υλοποίηση από την δοθείσα αρχική,διότι λογικά δε θα θέλουμε να εκτελεστεί ο κώδικας της func παρά μόνο όταν ζητήσουμε για πρώτη φορά την τιμή της Value.

Το πρόβλημα με τη συνθήκη της άσκησης να μη χρησιμοποιήσουμε καθόλου conditional logic είναι ότι conditional logic κρύβεται σε πολλά σημεία.Π.χ αν στην υλοποίησή μας χρησιμοποιούσαμε foreach θα μέτραγε για conditional logic (παρεπιπτόντως μέσω αυτής βρήκα την πρώτη μου υλοποίηση,η δεύτερη που σκέφτηκα είναι λίγο πιο μακροσκελής και χρησιμοποιεί περισσότερο funcional programming από ότι η δοθείσα αρχική κλάση).

Ιουνίου 7, 2010 1:00 πμ
 

darklynx έγραψε:

Το να εκτελέσεις την func στον constructor παρότι δείχνει βολική λύση είναι διαφορετική υλοποίηση από την δοθείσα αρχική,διότι λογικά δε θα θέλουμε να εκτελεστεί ο κώδικας της func παρά μόνο όταν ζητήσουμε για πρώτη φορά την τιμή της Value.

Το πρόβλημα με τη συνθήκη της άσκησης να μη χρησιμοποιήσουμε καθόλου conditional logic είναι ότι conditional logic κρύβεται σε πολλά σημεία.Π.χ αν στην υλοποίησή μας χρησιμοποιούσαμε foreach θα μέτραγε για conditional logic (παρεπιπτόντως μέσω αυτής βρήκα την πρώτη μου υλοποίηση,η δεύτερη που σκέφτηκα είναι λίγο πιο μακροσκελής και χρησιμοποιεί περισσότερο funcional programming από ότι η δοθείσα αρχική κλάση).

Ιουνίου 7, 2010 1:01 πμ
 

Markos έγραψε:

@darklynx: Δικό μου σφάλμα. Έχεις απόλυτο δίκιο. Υπό διαφορετικές συνθήκες ενδέχεται η μέθοδος που καλεί ο delegate να επιστρέψει διαφορετική τιμή για το ίδιο όρισμα, οπότε και η Value πρέπει να παίρνει την τιμή της όταν της ζητηθεί. Αυτό, όμως, δεν είναι λίγο επικίνδυνο; Ακόμα και μια φαινομενικά αθώα αντιστροφή της σειράς εκτέλεσης του κώδικα μπορεί να δώσει απρόσμενα αποτελέσματα αν μεταβάλει, για παράδειγμα, τιμές global παραμέτρων οι οποίες, όμως, χρησιμοποιούνται στην εν λόγω συνάρτηση. Πιο ασφαλές δεν είναι να δημιουργείται το object τη στιγμή που χρειάζεται και να υπολογίζεται η τιμή του property; Αυτό το σχόλιο, βέβαια, είναι ανεξάρτητο από το puzzle.

Ιουνίου 7, 2010 1:33 πμ
 

darklynx έγραψε:

@Markos: Εξαρτάται αν η μόνη δουλειά που κάνει η κλάση είναι να μας δίνει την τιμή της Value.Φαντάσου όμως η κλάση να έχει και επιπλέον λειτουργικότητα και ακόμα ο delegate που περνάμε στον contructor να παράγει σημαντικό φόρτο στο σύστημα,τότε έχουμε κάθε λόγο να αναβάλλουμε τον πρώτο υπολογισμό της value (να ακολουθήσουμε τρόπο τινά μια lazy προσέγγιση στο σχεδιασμό της κλάσης).

Τελικά βρήκα καμιά 4αρια προσεγγίσεις επί του ζητούμενου και μάλλον το παράκανα :P.Ορίστε οι δυο πρώτες,που αν και αισχρές ως σύλληψη δουλεύουν κατά το αναμενόμενο:

class Solution1<T>

   {

       private T value;

       private List<Func<T>> execList;

       public T Value

       {

           get

           {

               foreach (Func<T> func in execList)

                   value = func();

               execList.Clear();

               return value;

           }

       }

       public Solution1(Func<T> func)

       {

           execList = new List<Func<T>>();

           this.execList.Add(func);

       }

   }

class Solution2<T>

   {

       private T value;

       private Func<T> func;

       public T Value

       {

           get

           {

               value = func();

               func = () => { return value; };

               return value;

           }

       }

       public Solution2(Func<T> func)

       {

           this.func = func;

       }

   }

Ιουνίου 7, 2010 4:22 πμ
 

Markos έγραψε:

Και τι κακό έχουν οι μέθοδοι; Φτιάχνεις το object όποτε θες και τις καλείς όποτε θες. Κατά τη γνώμη μου, έτσι όπως είναι γραμμένος ο αρχικός κώδικας, ακόμα και ένα απλό refactoring μπορεί να δημιουργήσει απρόβλεπτες καταστάσεις. Θα είναι δύσκολο να διαγνώσεις το πρόβλημα όταν έχεις μια property που η τιμή της γίνεται finilized ύστερα από την πρώτη κλήση.

Η δεύτερη λύση πάντως είναι πιο όμορφη. Περιμένουμε να δούμε τι έστειλε ο Νίκος.

Ιουνίου 7, 2010 12:14 μμ
 

nxavar έγραψε:

Λύση βασιζόμενη σε Finite State Machine (καθώς δεν γνωρίζω F# ο κώδικας μπορεί να μην είναι συντακτικά ορθός).

Ενεργή κατάσταση: func

Δυνατές καταστάσεις: func_user, func_default

public class Thunk <T>

{

   private T value;

   private Func <T> func;

   private Func <T> func_user;

   private Func <T> func_default;

   private Func <T> func_user_core;

   {

       func  = func_user ( );

       return value;

   }

   private Func <T> func_user ( )

   {

       func  = func_default ( );

       value  = func_user_core ( );

       return value;

   }

   private

   public T value

   {

       get

       {

           return func ( );

       }

    }

   public Thunk ( Func <T> func )

   {

       func_user_core = func;

       this.func  = func_user;

   }

}

Οκτωβρίου 29, 2012 9:02 πμ

Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο)
απαιτούμενο
προαιρετικό
απαιτούμενο
ÅéóÜãåôå ôïí êùäéêü:
CAPTCHA Image