6 ms·
Ok so here's one way to solve that problem in FP: We define what we have first (after all this is mathematics): EmailList = a list of Email Email = a string
by innguest 12y ago
Ok so here's one way to solve that problem in FP:
We define what we have first (after all this is mathematics):
EmailList = a list of Email
Email = a string that goes "something" then "@" then "domain"
Now what we want:
DomainList, no duplicates.
Ok so no we're set up.
We want a function that takes us from an EmailList to DomainList, as defined above.
There are no objects so we simply say the relationships between things. I will introduce the tools we need:
map = takes a function and a list and applies that function to every element of the list, returning a new list.
We use map over EmailList with the function extractDomain which we will write later (it's trivial to do without mutation so I won't show here).
So now we have a list of domains, but there might be duplicates.
So now I have DomainList(with duplicates) and I want DomainList(without duplicates), so I want a function from DomainList to DomainList.
Well, what's the difference between what I have and what I want? What I have has too much, and I want less (ie. the list I have has too many domains (duplicates) and I want fewer (unique)).
filter = takes a list and a function. That function it takes, that you will supply to filter, we'll call f. Function f takes one list element (so filter will call f for every element) and it must return a boolean to mean whether that element should go into the new list, or if it should not go into the list.
So we could use filter to go from a DomainList with too much, to a DomainList with fewer things. However each time our function f is called, we don't know if this function has already been called before with that same domain; and because we don't have state to change a variable to keep a tally, it seems impossible.
So let's approach the problem in a different way. One thing we could do is sort the list, then group adjacent similar items, then map over the list taking the first element of each item. But we won't do that.
We need something more generic than filter. Let's look at fold.
fold = takes a list, a function f, and a starting point z (that we call 'zero').
For each element of the list, it calls your supplied function f with the current element and the zero; but then, it would be useless because it would be the same zero all the time. So what fold does, to be useful, is that whatever the result of calling your function f with a list item and the zero is, THAT value (so the value the call to yourFunctionF(list_item, zero) returns) will be the next 'zero'. So now we have a little machine called fold that can thread something we have through its inner mechanism and play along with us.
Using that we can say the zero is the list of elements we've seen so far.
So now our DomainList -> DomainList function will be a fold. That fold will take a the DomainList we have, and a zero that will be the empty list (to start with!) and a function f. That function f will simply check if the item is inside zero. If it is, then simply return zero (because that will be the zero for the next call to f!); if it's not, then return a new zero with that item added to it.
Now we've achieved the goal without any mutation (ie. without us talking/concerning ourselves with any mutation. of course there's lots of mutation going on in RAM but the point is not to eliminate that!)
Does that help?
- chris_mahan 12y agoSadly, no. It's like I asked for a tuna sandwich and you explained how to build the engine of the fishing boat to go catch the tuna off the coast of Baja. I feel like I need a masters in computer science to understand what you wrote. let me see: # Python: domains = [] emails = open("file_with_emails.txt","r").readlines() for email in emails: ____domain = email.split("@")[1] ____if domain not in domains: ________domains.append(domain) out = open("file_with_domains.txt","w") for domain in domains: ____out.write(domain) out.close() input: joe@msn.com mike@aol.com tim@aol.com sam@hotmail.com output: msn.com aol.com hotmail.com Simple no?
- innguest 12y agoThat's actually not simple at all because it assumes an implementation, that is, a machine with RAM that changes. For instance, what does = mean between domain and email.split ? Who is equal to who? That question is a question that students sometimes have. You have to explain that Time is implicit in the code, and that an equal sign works by making the thing on the left equal to what the thing on the right was just before this line. So now there's a concept of Time and that's how things start to get very complicated, because it is implicit in your code. Then, what does ".append" mean? I mean, you call append, but now you don't assign the result to any variable? And what would it mean if you said domains = domains.append(domain), then we really need to have a talk about the hidden Time. So it only seems simple because it's familiar to you; so in fact I believe it's not simple at all. But this I believe it's simple, and my fishing boat engine above would've benefited from this: https://www.fpcomplete.com/tutorial-preview/4217/4k7kq6jGUE https://www.fpcomplete.com/tutorial-preview/4217/4k7kq6jGUE If you look at that code (you can also click play to run it) and then look at the explanation again, could you please tell me what doesn't seem simple? Edit: let's not forget your Python example uses a map, disguised as a for loop. That for is a keyword, but backstage it's just map where email is the element, emails is the list, and everything you write inside that scope is the function that gets passed the element each time (except in your case the function (i.e. the loop body) can also see the whole list, which is prone to bugs).
- chris_mahan 12y ago