Open Side Menu Go to the Top

04-12-2015 , 02:49 AM
Quote:
Originally Posted by gaming_mouse
yeah, the login functions are a mess.

pretty much whenever you have stuff like:

Code:
if (blah)
   //long mess of crap
else
   // long mess of crap
you can improve it by pulling the long bunches of crap into their own functions, so you have:

Code:
if (blah)
   conciseFunctionNameDescribingWhatMessofCrapDoes();
else
   anotherDescriptiveFunctionName();
The complaint that "now I have to jump to another function to see what it does" is misguided. One, your editor should make that trivial. But two, and this is more important, your mind only focuses on one level of abstraction at a time. That is, either you trying to understand what the function does on a high level, in which case the refactored version is much clearer, because the details of the messes of crap are noise; or you are interested in the details of one of those messes of crap, in which case you'd rather be focusing on just that, and viewing it 3 or 4 indentations deep within a context that is not relevant for your current level of focus is just noise.
I do this all the time and my C++ teacher hates it. particularly when I have to use a nested loop to search for something inside of some other thing I was already iterating through, I just make a damn search() function and place it inside my loop. Way easier to read and trace.

If you make your "helper" functions short and concise it does not make the program more complicated to read at all.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD **
$25m Guaranteed WPM on CoinPoker
Join the action now
Daily Rewards • Splash Pots • CoinRaces
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD **
04-12-2015 , 04:11 AM
Quote:
Originally Posted by jmakin
particularly when I have to use a nested loop to search for something inside of some other thing I was already iterating through, I just make a damn search() function and place it inside my loop.
he's probably right with this part of his objection. an example would help (not sure i understand you completely), but if a function inside a loop needs access to some data the loop iteration already has a handle on, why not just pass the data in as an argument?
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 05:04 AM
Quote:
Originally Posted by daveT
The code generation is an extension of this:

...

So, I have all of these files that make it obvious what you are getting, but I don't think having them there does anything for the program. I have no intention of ever using them. Updating the system would be a total nightmare if I constantly pushed to .sql files and attempted to run update scripts like this. I just don't see the point of having the .sql files at all, but from an end-user perspective, I wouldn't know what I am getting without some sort of reference material, and who the hell wants to read a bunch of UML documentation?
Interesting, thanks for the links. For some reason it really surprises me that Amazon uses .xls files for this stuff. When you mentioned generating LOCs I was thinking an entirely different direction. Generated SQL statements don't really qualify as "generated code" in my mind, but I guess it's technically correct.

Didn't look into it that much, but from what I understand I'd just treat the .sql files as output/results, not as part of the project. I think the standard approach would be to only keep a few small .sql files in the project as documentation/example and get rid of the rest. Maybe add a visualization of the generated db schema. (That would of course require that you can easily re-create these sql files without any manual fiddling.)
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 01:38 PM
Depends on how deep the reasoning stack gets. If I have 7 LOC but they contain functions that contain functions etc. I tend to prefer making some of them longer to avoid going deeper (if that makes sense).
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 02:11 PM
Doesn't as a sex joke
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 02:34 PM
Quote:
Originally Posted by Anais
Doesn't as a non-computational parallel processing joke
.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 03:20 PM
Quote:
Originally Posted by gaming_mouse
do you mean the function is essentially setting a bunch of config variables, each on their own line?

i think that's a valid exception to the rule, although you could potentially group the config variables by categories, or you could do the config in config file. but it's far less important in that case.
Here you go - knock yourself out:

Code:
  // attach to app.locals so errorHandlers can use it in altenate flow
  app.locals.appFinish = function(req, res, next) {
    debug("appFinish called");
    res.emit('timer', 'appFinish->' + req.nodule.name);

    var renderData = res.yukon.renderData;

    // non-components such as JSON calls and cms widgets dont get appClientData
    if (!req.nodule.isComponent) {
      if (!req.isHealth) {
        renderData.reporting = reporting.process(req.nodule.isJson, req, res);
        renderData.messagekeys = res.messagekeys;
        renderData.contingencies = res.contingencies;
      }

      doLog(req, res);
      
      next();
      return;
    }

    
    // passing request path for SEO canonical tag
    if (req.path.indexOf('__') !== -1 && req.cmsPath) 
      renderData.path = req.cmsPath;
    else 
      renderData.path = req.path;

    if (!res.locals.appClientData)
      res.locals.appClientData = {};
    
    if (!res.yukon.userProfile)
      res.yukon.userProfile = { profile:{} }; // something went wrong with getProfile call

    res.locals.appClientData.contingencies = res.contingencies; // note these are messages from the server - to be consumed by an angular directive, we may use a jade template instead

    res.locals.appClientData.messagekeys = res.messagekeys; // these are ATG messages with their keys that need to be handled by front end templates or JS code

    // Notes: This comes from Scientia - which is not installed on our dev boxes. To spoof this information on dev boxes - use lib/mobilesim.js.
    //        DeviceResolution only seems to be accurate for phones and tablets.
    res.locals.appClientData.deviceData = {
      type:        req.deviceType,
      cmsType:     res.locals.cmsDeviceType,
      os:          req.headers['x-device_device_os'],
      brand:       req.headers['x-device_brand_name'],
      iosVersion:  res.locals.iosVersion,
      resolution:  req.headers['x-device_resolution_width'] + 'x' + req.headers['x-device_resolution_height'],
      isSupported: req.isSupportedDevice
    };

    if (res.yukon.fullSiteNav) 
      renderData.fullSiteNav = res.yukon.fullSiteNav;
        
    if (res.yukon.mobileNav) 
      res.locals.appClientData.mobileNav = res.yukon.mobileNav;

    try { 
      res.locals.appClientData.reporting = reporting.process(req.nodule.isJson, req, res);
    } catch (error) {
      req.errLog.error(debug(error.stack || error));
    }

    if (req.isModal) 
      renderData.isModal = true;
    
    if (req.query.overlay) 
      renderData.isOverlay = res.locals.appClientData.isOverlay = true;
    else 
      renderData.isOverlay = res.locals.appClientData.isOverlay = false;

    if (res.yukon.userProfile && res.yukon.userProfile.profile) {
      res.locals.appClientData.userProfile = {};
      // customer's etoken is bad or expired, we need to set them back to prospect status
      if (res.yukon.userProfile.profile.customer && !res.yukon.userProfile.profile.authorized) {
        res.yukon.userProfile.profile = _.clone(app.libUtils.defaultProfile);
        res.clearCookie('JSESSIONID', {domain:'xxx.com'});
        res.clearCookie('DYN_USER_ID', {domain:'xxx.com'});
        res.clearCookie('DYN_USER_CONFIRM', {domain:'xxx.com'});
        res.clearCookie('customer', {domain:'xxx.com'});

        var msg = moment().format('M[/]D') + 'E-token mismatch, profile.authorized = false, resetting user to prospect';
        req.errLog.error(msg);
        debug(msg);
        app.libUtils.updateAggregateErrors(msg);
      }

      _.each(clientUserProfileVars, function(property) { 
        res.locals.appClientData.userProfile[property] = res.yukon.userProfile.profile[property]; 

// uncomment this to debug user profile, don't check it in uncommented
//        debug("userProfile." + property + " = " + res.locals.appClientData.userProfile[property]);

      });
      
      debug('requestId = ' + req.requestId);
      debug("sessionId = " + req.sessionId);
      debug("DYN_USER_ID = " + req.cookies['DYN_USER_ID']);

      if (req.cookies['appe-prospect-zip'] && res.locals.appClientData.userProfile.customer) 
        res.clearCookie('appe-prospect-zip');

      res.locals.appClientData.userProfile.internalStreamingDisabled = res.locals.internalStreamingDisabled;
    }

    // slimmed down version of user profile is good for most templates
    renderData.userProfile = res.locals.appClientData.userProfile;

    // modal launched via deep link (after user signs in, could be used elsewhere)
    if (req.query.modal) {
      res.locals.appClientData.modal = {
        modalClose: req.query.modalClose,
        modalTitle: req.query.modalTitle,
        modalClass: req.query.modalClass,
        modalError: req.query.modalError,
        modalTarget: req.query.modal,
        modalQueryString: querystring.stringify(_.omit(req.query, ['modal','modalClose','modalTitle','modalClass','modalError']))
      };
    }

    // Build error query for URL helpers
    var requestUrl = url.parse(req.originalUrl);
    var browserUrl = req.protocol + '://' + req.headers.host + requestUrl.pathname;
    var parsedQuery = querystring.parse(req.query);
    parsedQuery.modal = '/modal/signin';
    parsedQuery.modalError = 'true';
    var errorQuery = querystring.stringify(parsedQuery);
    var cdnHost = (requestUrl.protocol === 'http') ? "http://cdn.xxx.com" : "https://cdns.xxx.com";
    var appendQueryString =  req.query ? '?'+ querystring.stringify(req.query) : '';

    res.locals.loginDargs = app.config.login_dargs;

    // URL Helper Object for client
    res.locals.appClientData.url = {
      referer: req.headers.referer ? url.parse(req.headers.referer) : undefined,
      loginServer: app.locals.loginServer,
      // teamSitePath: req.protocol+app.locals.teamsiteServer.substr(app.locals.teamsiteServer.indexOf(':')),

      logoutLink: app.locals.loginServer + app.config.logout_url,
      appImageUrl: (requestUrl.protocol === 'http') ? app.config.app_http_img_url : app.config.app_https_img_url,
      appHomePage: app.config.app_home_page,
      cdnHost: cdnHost,
      is_prod_env: app.config.is_prod_env,
      enabledFlag: app.locals.atgConfig.enabled,
      typeAheadUrl: app.config.typeAheadUrl,
      sessionTimeoutWarning: app.locals.atgConfig.guardAuthEnabled ? app.config.session_timeout_minutes_guard : app.config.session_timeout_minutes
    };

    // URL Helper Object for JADE
    renderData.url = _.clone(res.locals.appClientData.url); // shallow clone is ok here
    renderData.url.request = requestUrl;
    renderData.url.request.host = req.hostname; // Request object is not properly populating these values
    renderData.url.request.protocol = req.protocol; // Request object is not properly populating these values

    renderData.url.loginSuccessUrl = browserUrl + appendQueryString;
    renderData.url.loginFailUrl = browserUrl + '?' + errorQuery;
    renderData.url.query = req.query; // don't blindly pass the query to client for XSS reasons
 
    renderData.hostname = os.hostname();
    renderData.userId = req.cookies['DYN_USER_ID'] || 'ANON';
    renderData.sessionId = req.sessionId;
    renderData.requestId = req.requestId;

    var isIE = req.headers['user-agent'].match(/MSIE (\d+)\./);
    renderData.ieVersion = res.locals.appClientData.deviceData.ieVersion = (isIE) ? 1*isIE[1] : false;

    // Make crawler detection available to JADE
    renderData.isCrawler = req.isCrawler;
  
    if (app.appMiddleware.appFinish) app.appMiddleware.appFinish(req, res);

    doLog(req, res);

    req._timer.stop();

    if (req.nodule.error !== 'isRedirect') next();    
  };
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 03:31 PM
To be fair that function is the longest in our whole app by a long shot. It's just a laundry list of **** that's needed for jade and angular to render our monster site. I could always break it into little functions but they'd never be re-used anywhere else.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 05:13 PM
That to me is a perfect example of a function that would be trivially better being broken up. Taking a quick look, most chunks of code under a comment could be an individual function and it would probably be trivially easier to understand.

But since most of that crap isn't going to be reused I don't have a problem with the one big method.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 07:25 PM
Quote:
Originally Posted by suzzer99
Thanks. Yeah I'd definitely be looking at it as a 2-year sacrifice to help my career/boost my development as an enginer. But of course it could always turn into more.

They seem kinda weird about tele-commuting. Does your friend have any input on that?
My friend drives from Redwood city, so he is in the office most days. Not sure about their policy for working from home a ton.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 08:58 PM
Quote:
Originally Posted by plexiq
Interesting, thanks for the links. For some reason it really surprises me that Amazon uses .xls files for this stuff.
It is kind of interesting. They have a plug-in that would allow you to upload into amazon without ever leaving the excel sheet. I'm not sure what other format they would use for data organization and uploading. I mean, you have to get it into their database somehow, but in general, you would use a tab-delimited text file, which incidentally, is how you interact with their silly "API," which is functionally the same as doing a manual upload via the user interface, albeit using whatever is analogous to curl.

Quote:
When you mentioned generating LOCs I was thinking an entirely different direction. Generated SQL statements don't really qualify as "generated code" in my mind, but I guess it's technically correct.
Well, yeah and no. I mean, it is stuff that could be done by hand but I'd rather not. I was using this rather extreme example, which I suppose is obviously correct, but I have my real example down below.

Quote:
Didn't look into it that much, but from what I understand I'd just treat the .sql files as output/results, not as part of the project. I think the standard approach would be to only keep a few small .sql files in the project as documentation/example and get rid of the rest. Maybe add a visualization of the generated db schema. (That would of course require that you can easily re-create these sql files without any manual fiddling.)
Right, I could pull all the relationships (PK, FK) out of of the information schema, but I'd still have to figure out a good way to make it presentable. Probably just pull it out and render it via d3.js or something simple like that.

The real example is this, and I have this pattern all through this one page of code. After refactoring it to death, I found a common pattern:

Code:
 (defn trips [hand-map]
  (let [t-cards (ranks-strength-count hand-map 3)
        k-cards (sorted-ranks-strength-count hand-map 1)]
    (conj t-cards
          (take-last 2 k-cards))))

(defn quads [hand-map]
  (let [q-cards (ranks-strength-count hand-map 4)
        k-cards (sort (concat (ranks-strength-count hand-map 1)
                              (ranks-strength-count hand-map 2)
                              (ranks-strength-count hand-map 3)))]
    (conj q-cards
          (take-last 1 k-cards))))
This is a rough translation to Python:

Code:
## not exactly how this work, but trying to make it clearer. 
## In reality the functions return lists and I cons them together.
def trips (hand_map):
    t_cards = ranks_strength_count (hand_map 3)
    kicker_cards = sorted_ranks_strength_count (hand_map 3)
    return [[kicker_cards], t_cards]

def quads (hand_map):
    q_cards = ranks_strength_count (hand_map 4)
    ## only one of these function will return a value. Not sure if
    ## Null + Null + value = ( , , value), but the intention is for it
    ## to be (value).
    kicker_cards = sorted_ranks_strength_count (hand_map 1) +
                   sorted_ranks_strength_count (hand_map 2) +
                   sorted_ranks_strength_count (hand_map 3)
    return [[kicker_cards] q_cards]
While these *are* different, trips and pair are nearly identical, with the only difference being the numerical arguments:

Code:
(defn pair [hand-map]
  (let [p-card (ranks-strength-count hand-map 2)
        k-cards (sorted-ranks-strength-count hand-map 1)]
    (conj p-card (take-last 3 k-cards))))
So, considering some foresight and discovering this general pattern early, is there a point where we can just say "generate" or make this even more abstract, so that the functions are returned, but aren't visible in the code?
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 09:15 PM
what are "hand-map" and "ranks-strength-count" and "sorted-ranks-strength-count"? my scheme is very rusty -- could you also explain the algorithm in english?
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 09:53 PM
I saw this somewhere else on this forum too, but overabstracting is usually a bad habit in general, but it becomes more absurd when you're dealing with games like poker and chess where the rules are pretty much fixed. Usually forcing yourself to overabstract and overfactor makes you technically better because it makes the problem you're solving technically more challenging than it should be. Unfortunately, it also makes your code base more challenging for others to understand. This is fine for learning, but in practice, when programmers start practicing this without a long-term understanding of how the system's requirements are likely to evolve, it results in highly abstract code bases that are difficult to maintain and are only extensible in ways no one cares about.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 09:53 PM
The hand-map is a key-value store of hands to quantity of hands. In this example, there are two queens, one ten, one ace, etc.

Code:
{T 1, Q 2, A 1, 6 1, J 1, 2 1}
ranks-strength-count is a function that is a combination of functions.

The first function converts the strings to numerical values (using a sort of "pointer" to the ranks list)

Code:
(def ranks ["_" "2" "3" "4" "5" "6" "7" "8" "9" "T" "J" "Q" "K" "A"])
;; Q is in position 11
the second function takes the value-key and matches the integer argument. In this case, I am asking what "card rank" appears twice. The final result is a list (seq) of numbers.

the result is (11) because Q is in position 11 on the ranks initialization list.

The k-cards follow the same exact idea, but now I'm asking for the list of keys with a value of 1 and I want this list to be sorted. This returns

Code:
(1 5 9 10 13)
Since only 5 cards matter, I have to remove some of this, and the easist way to to take the last N cards I need. I have used up for the pair of Queens, so I only need to take 3 of the above.

Finally, I want to cons the list so I get the following result:

Code:
((9 10 13) 11)
Where the 11 is the hand, and there are kickers in the first list. This is just test stuff for the moment. in reality the final result will look like this (assuming I want to keep it the same as it is already working):

Code:
[((9 10 13) 11) :pair]
This is the code with print statements:

Code:
(defn pair [hand-map]
  (println hand-map) ;; {T 1, Q 2, A 1, 6 1, J 1, 2 1}
  (let [p-card (ranks-strength-count hand-map 2)
        k-cards (sorted-ranks-strength-count hand-map 1)]
    (println p-card) ;; (11)
    (println k-cards) ;; (1 5 9 10 13)
    (conj p-card (take-last 3 k-cards)))) ;; result ((9 10 13) 11)
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:02 PM
Quote:
Originally Posted by candybar
I saw this somewhere else on this forum too, but overabstracting is usually a bad habit in general, but it becomes more absurd when you're dealing with games like poker and chess where the rules are pretty much fixed. Usually forcing yourself to overabstract and overfactor makes you technically better because it makes the problem you're solving technically more challenging than it should be. Unfortunately, it also makes your code base more challenging for others to understand. This is fine for learning, but in practice, when programmers start practicing this without a long-term understanding of how the system's requirements are likely to evolve, it results in highly abstract code bases that are difficult to maintain and are only extensible in ways no one cares about.
I'm trying to figure out where the line is. In both examples, I lose information, but at what point is it too much?

In the first example, I'm losing the information of 1,000+ tables, but the code (whether or not you think of SQL as code) is still "written" and still impacting the system. It surely wouldn't be clear at alll what is happening from the python code.

The second example looks at this from the other end. Here I have a small-ish code base, but a significant number could be resolved to a single function. I don't think extending that would be much of a challenge at all. "Adding" one function would be a matter of adding a sinlge line of code.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:12 PM
I have no idea what you're trying to do but how does it exactly help anything at all to factor the commonality between your pairs and trips functions? What real world problems are you trying to solve?

This was the other one I was thinking of:

http://forumserver.twoplustwo.com/sh...&postcount=400
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:18 PM
Thanks, the code with the print statements made it clear.

So I think I now understand your original point is that this algorithm is the same for trips, except with trips, for "p-card", you use "hand-map 3" and at the end you "take-last 2" instead of "take-last 3", correct?

Rather than taking the nuclear option of writing macro code to generate the two similar functions, what about just making those things which are different parameters to a general case function?

with your approach, it seems like the natural generalization is a "signature" of ranks. pair = (2,1,1,1,1,1), trips = (3,1,1,1,1), quads = (4,1,1,1), two pair = (2,2,1,1,1), full house = (3,2,1,1). i'm oversimplifying some, since eg quads could also be (4,2,1), etc. but essentially, each hand category is defined by a set of such signatures. and the process of extracting the 5-card, correctly kickered hand is straightforward once you know which signature case you are in -- what you've already implemented would extend to the general case with little modification.

I do think candybar makes a good point, over-abstraction is as real a problem as repetitive, unfactored code.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:28 PM
Quote:
Originally Posted by candybar
I have no idea what you're trying to do but how does it exactly help anything at all to factor the commonality between your pairs and trips functions? What real world problems are you trying to solve?
i think there's always value to making your code shorter and more readable.

sure, it's less important the less likely change is, but still....
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:29 PM
that is correct, gaming_mouse. The initial hand is just a list of lists as [[Rank Suit] [Rank Suit] [Rank Suit] ...].

There is a frequency function that computes the lists as you are saying, which then sends the hand-map to the correct function (rather collection of functions since (1 1 1 1 1 1 1) can be a high-card, straight, or flush).

In the two-year old version (wow, really?), I was doing that check for each hand in each function, then stopping at the first correct function. The dispatching version is much cleaner but also a bit more tricky. I could, in theory, dispatch the hand along with the integer differences and covert the let-binding functions into variadic functions, but I think that may be a tad bit too extreme in this exact case, but if I needed 100 of these functions, then it would make sense, right?

The dispatch is here: https://github.com/dt1/poker-bots/bl...h.clj#L133-181
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:30 PM
With that said, if you probably only need sorted-ranks-strength-count (you don't need ranks-strength-count as far as I can tell) and if you change it so that you can extract some cards and get the remainder, you can pretty much use the same pattern for trips/quads/pairs.

Also, I think you have more of a data structure and algorithm problem here more than anything else.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:37 PM
Quote:
Originally Posted by gaming_mouse
i think there's always value to making your code shorter and more readable.

sure, it's less important the less likely change is, but still....
The problem is that if the underlying algorithm isn't properly abstracted, or if you don't use good names or if you mistook coincidental similarity for genuine similarity, it's less readable, not more. And after 15+ years of reading other people's code, I don't have a lot of faith that these things will be done properly. And people who can do all of this well simply don't need to be told anything, which is why I think any rule that steers people towards overabstraction is dangerous. It's kind of like how if your son says he wants to become a professional musician, you should always tell him that's a terrible idea. It's not that it's a terrible idea for everyone, but it's a terrible idea for everyone who has to rely on other people's judgement.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:44 PM
Quote:
Originally Posted by candybar
The problem is that if the underlying algorithm isn't properly abstracted, or if you don't use good names or if you mistook coincidental similarity for genuine similarity, it's less readable, not more. And after 15+ years of reading other people's code, I don't have a lot of faith that these things will be done properly. And people who can do all of this well simply don't need to be told anything, which is why I think any rule that steers people towards overabstraction is dangerous. It's kind of like how if your son says he wants to become a professional musician, you should always tell him that's a terrible idea. It's not that it's a terrible idea for everyone, but it's a terrible idea for everyone who has to rely on other people's judgement.
sure, but dave is specifically trying to learn and improve his code by thinking about it and experimenting. "you're overthinking it" isn't the right response in that situation.

fwiw, the rank signature / dispatch thing doesn't seem complex or overly abstract to me. it feels like the natural abstraction. i don't follow all of the scheme implementation details, but the concept seems sound.
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:48 PM
Quote:
Originally Posted by gaming_mouse
with your approach, it seems like the natural generalization is a "signature" of ranks. pair = (2,1,1,1,1,1), trips = (3,1,1,1,1), quads = (4,1,1,1), two pair = (2,2,1,1,1), full house = (3,2,1,1). i'm oversimplifying some, since eg quads could also be (4,2,1), etc. but essentially, each hand category is defined by a set of such signatures.
If you use pattern matching:

quads -> 4, x, _ -> hand = 4 :: (head x)
fh -> 3, 2, _ -> hand = 3 :: 2
trips -> 3, y -> hand = 3 :: (take 2 y)
twopair -> 2, 2, _ -> hand = 2 :: 2 :: (head x)
pair -> 2, _ -> hand = 2 :: (take 3 y)

and so on.

As long as the full hand comes presorted, this is entirely trivial
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:56 PM
Quote:
Originally Posted by candybar
If you use pattern matching:

quads -> 4, x, _ -> hand = 4 :: (head x)
fh -> 3, 2, _ -> hand = 3 :: 2
trips -> 3, y -> hand = 3 :: (take 2 y)
twopair -> 2, 2, _ -> hand = 2 :: 2 :: (head x)
pair -> 2, _ -> hand = 2 :: (take 3 y)

and so on.

As long as the full hand comes presorted, this is entirely trivial
yeah i like that. fh could be 3, 3 too, but basically this is what i was thinking. don't remember if scheme has built in matchers like that or not tho....
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
04-12-2015 , 10:59 PM
Quote:
Originally Posted by gaming_mouse
fwiw, the rank signature / dispatch thing doesn't seem complex or overly abstract to me. it feels like the natural abstraction. i don't follow all of the scheme implementation details, but the concept seems sound.
You have to pick a proper abstraction at the proper level. Where you went is fine. Where he was, wasn't.

Quote:
sure, but dave is specifically trying to learn and improve his code by thinking about it and experimenting. "you're overthinking it" isn't the right response in that situation.
http://www.acmuller.net/con-dao/analects.html#div-12

Quote:
Zi Lu asked if it was a good idea to immediately put a teaching into practice when he first heard it.

Confucius said, “You have a father and an older brother to consult. Why do you need to be so quick to practice it?”

Zanyou asked the same question. Confucius said, “You should practice it immediately.”

Gong Xihua said, “When You asked you, you told him he should consult his father and elder brother first. When Qiu (Zanyou) asked you, you told him to practice it immediately. May I ask why?”

Confucius said, “Qiu has a tendency to give up easily, so I push him. You (Zi Lu) has a tendency to jump the gun, so I restrain him.”
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD ** Quote
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD **
$25m Guaranteed WPM on CoinPoker
Join the action now
Daily Rewards • Splash Pots • CoinRaces
** UnhandledExceptionEventHandler :: OFFICIAL LC / CHATTER THREAD **

      
m