Convert text prediction script [Markov Chain] from javascript to python - javascript

i've been trying the last couple days to convert this js script to python code.
My implementation (blindfull cp mostly, some minor fixes here and there) so far:
import random
class markov:
memory = {}
separator = ' '
order = 2
def getInitial(self):
ret = []
for i in range(0, self.order, 1):
ret.append('')
return ret
def breakText(self, txt, cb):
parts = txt.split(self.separator)
prev = self.getInitial()
def step(self):
cb(prev, self.next)
prev.shift()#Javascript function.
prev.append(self.next)
#parts.forEach(step) # - step is the function above.
cb(prev, '')
def learn(self, txt):
mem = self.memory
def learnPart(key, value):
if not mem[key]:
mem[key] = []
mem[key] = value
return mem
self.breakText(txt, learnPart)
def step(self, state, ret):
nextAvailable = self.memory[state] or ['']
self.next = nextAvailable[random.choice(nextAvailable.keys())]
if not self.next:
return ret
ret.append(next)
nextState = state.slice(1)
return self.step(nextState, ret)
def ask(self, seed):
if not seed:
seed = self.genInitial()
seed = seed + self.step(seed, []).join(self.separator)
return seed
Issues:
I have absolutely no knowledge of javascript.
When i try to "learn" some text to a "markov" class object [e.g.: a=markov(); a.learn("sdfg");] i get the following error: "TypeError: unhashable type: 'list'", for the "mem" dictionary at the "learnPart" function, member of the "learn" function.
So my question so far is why does this exception [TypeError for a list object, falsely referring to a dictionary object (which is hashable)] occur?
thanks in advance for any suggestions, directions, points, help in general :D

Guy who wrote the article speaking. Glad you found it useful! Now, my first implementation of a Markov chain was actually in Python, so this answer will focus on how to write it in a more Pythonic way. I'll show how to go about making an order-2 Markov chain, since they're easy to talk about, but you can of course make it order-N with some modifications.
Data Structures
In js, the two prominent data structures are the generic object and the array (which is an extension to the generic object). In Python however, you have other options for more finely-grained control. Here're the major differences in the two implementations:
A state in our chain is really a tuple - an immutable, ordered structure, with a fixed amount of elements. We always want n elements (in this case, n=2) and their order has meaning.
Manipulating the memory will be easier if we use a defaultdict wrapping a list, so we can skip the "checking if a state doesn't exist, and then doing X", and instead just do X.
So, we stick a from collections import defaultdict at the top and change how markov.memory is defined:
memory = defaultdict(list)
Now we change markov.getInitial to return a tuple (remember this explains an order-2 chain):
def getInitial(self):
return ('', '')
(if you want to expand it further, you can use a really neat Python trick: tuple([''] * 2) will return the same thing. Instead of empty strings, you can use None)
We'll get to changing what uses genInitial in a bit.
Yield and iteration
A strong concept which doesn't exist in js (yet) but does exist in Python is the yield operator (see this question for great explanations).
Another feature of Python is its generic for loop. You can go over nearly anything quite easily, including generators (functions which use yield). Combining the two, and we can redefine breakText:
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
#will be explained in the next paragraph
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield prev, ''
The magic part above, prev = (prev[1], word) can be explained best by example:
>>> a = (0, 1)
>>> a
(0, 1)
>>> a = (a[1], 2)
>>> a
(1, 2)
That's how we advance through the word list. And now we move up to what uses breakText, to the redefinition of markov.learn:
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
Because our memory is a defaultdict, we don't have to worry about the key not existing.
A pee break on the side of the road
OK, we have half of the chain implemented, time to see it in action! What we have so far:
from collections import defaultdict
class Markov:
memory = defaultdict(list)
separator = ' '
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield (prev, '')
def getInitial(self):
return ('', '')
(I changed the class name from markov to Markov because I cringe every time a class begins with a lowercase letter). I saved it as brain.py and loaded up Python.
>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.memory
defaultdict(<class 'list'>, {('had', 'a'): ['little'], ('Mary', 'had'): ['a'], ('', ''): ['Mary'], ('little', 'lamb'): [''], ('a', 'little'): ['lamb'], ('', 'Mary'): ['had']})
Success! Let's look at the result more carefully, to see that we got it right:
{ ('', ''): ['Mary'],
('', 'Mary'): ['had'],
('Mary', 'had'): ['a'],
('a', 'little'): ['lamb'],
('had', 'a'): ['little'],
('little', 'lamb'): ['']}
zips up Ready to drive on? We still have to use this chain!
Changing the step function
We've already met what we need to remake step. We have the defaultdict, so we can use random.choice right away, and I can cheat a bit because I know the order of the chain. We can also get rid of the recursion (with some sorrow), if we see it as a function which takes a single step through the chain (my bad in the original article - a badly named function).
def step(self, state):
choice = random.choice(self.memory[state] or [''])
if not choice:
return None
nextState = (state[1], choice)
return choice, nextState
I regretfully added the or [''] because random.choice moans about empty lists. Finally, we move a larger portion of the logic to ask (the actual construction of the sentence):
def ask(self, seed=False):
ret = []
if not seed:
seed = self.getInitial()
while True:
link = self.step(seed)
if link is None:
break
ret.append(link[0])
seed = link[1]
return self.separator.join(ret)
Yes, a bit yucky. We could have given step a better name and made it a generator, but I'm late for a meeting with my pregnant wife who's about to give birth to a baby who left the stove on fire in my car that's being towed! I better hurry!
The grand finale
But first, a talk with bob:
from collections import defaultdict
import random
class Markov:
memory = defaultdict(list)
separator = ' '
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
def ask(self, seed=False):
ret = []
if not seed:
seed = self.getInitial()
while True:
link = self.step(seed)
if link is None:
break
ret.append(link[0])
seed = link[1]
return self.separator.join(ret)
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield (prev, '')
def step(self, state):
choice = random.choice(self.memory[state] or [''])
if not choice:
return None
nextState = (state[1], choice)
return choice, nextState
def getInitial(self):
return ('', '')
And loading it up:
>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.ask()
'Mary had a little lamb'
>>> bob.learn('Mary had a giant crab')
>>> bob.ask(('Mary', 'had'))
'a giant crab'
There is, of course, room for improvement and expanding on the concept. But it wouldn't be any fun if if I just gave you the answer.
Hopefully this will still help after 4 months.

The complex answer
The issue here is that learnPart is trying to use the return value of getInitial, which is a list, as a key to a dictionary. Lists are mutable, and hence not hashable, which means they can't be used as keys to a dictionary.
You could try adding this line to learnPart:
def learnPart(key, value):
key = tuple(key) #<-----Try adding this line
if not mem[key]:
mem[key] = []
mem[key] = value
return mem
But I do not think that will solve all the problems.
The simple answer
There are plenty of Markov Chain implementations written in Python out there. A quick search on Github yielded 168 projects:
https://github.com/search?l=Python&q=markov+chain

I kind of made a simplified version of the code:
import re
class Brain():
H = ''
def learn(self, txt):
self.H = txt
def ask(self,ask):
H=self.H
ask = re.compile(r"%s(.*)"%(ask),re.I|re.DOTALL)
m = ask.search(H)
print m.group(1)
Here's the execution:
>>> import brain
>>> bob = brain.Brain()
>>> bob.learn('Mary had a little lamb' )
>>> bob.ask('Mary had')
'a little lamb'
I agree that this isn't exactly a Markov chain algorithm.But it has a few advantages:
i. You can supply ask() with raw text as shown above.
ii. It has a fewer lines of code.
iii. And hopefully, it is easier to understand.

Related

Are there ways of easily sorting a list of objects like this in Javasript?

I have a Node class
class Node:
def __init__(self, move = None, parent = None, state = None):
self.move = move
self.parentNode = parent # "None" for the root node
self.childNodes = []
self.wins = 0
self.visits = 0
self.untriedMoves = state.GetMoves() # future child nodes
self.playerJustMoved = state.playerJustMoved # the only part of the state that the Node needs later
def UCTSelectChild(self):
""" Use the UCB1 formula to select a child node. Often a constant UCTK is applied so we have
lambda c: c.wins/c.visits + UCTK * sqrt(2*log(self.visits)/c.visits to vary the amount of
exploration versus exploitation.
"""
s = sorted(self.childNodes, key = lambda c: c.wins/c.visits + sqrt(2*log(self.visits)/c.visits))
return s
I have a list of these Node objects.
In Python I can easily sort like so..
s = sorted(self.childNodes, key = lambda c: c.wins/c.visits + sqrt(2*log(self.visits)/c.visits))
Best way to do this in Javascript? This is my first day using Javascript..
You can use the sort() method of the Array.
array = [...];
array = array.sort();
This will sort the array based on the default comparison logic. If you want to provide your own comparison logic, you can just pass a compare function as an argument. Unlike Python's key where you consume just one parameter, the compare function provides firstElement and secondElement as the parameters. So, you might do something like this:
array = array.sort((firstEl, secondEl) => {
let first = firstEl.wins / firstEl.visits + sqrt(2 * Math.log(firstEl.visits)/firstEl.visits);
let second = secondEl.wins / secondEl.visits + sqrt(2 * Math.log(secondEl.visits)/secondEl.visits);
return first - second;
});
Surely, this seems a bit more verbose but that's how it is.

Comparing 2 Json Object using javascript or underscore

PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.

crockjs and apply functions need a function as value

I'm actually learning functional programming, and I'm trying to learn & use crockjs
For now, I'm trying to implement the "monadster" program described in https://fsharpforfunandprofit.com/monadster/ .
Here's what I'm having for now (just the beginning...)
const State = require("crocks/State");
const LivingPart = (unitOfForce, deadThing) => ({ unitOfForce, deadThing });
// Creating the potential living thing
const makeLiveThingM = deadThing => {
const becomeAlive = vitalForce => {
const unitOfForce = 1;
const remaining = vitalForce - unitOfForce;
return { part: LivingPart(unitOfForce, deadThing), remaining };
};
return State.get(becomeAlive);
};
// Using containers
const deadLegM = makeLiveThingM("deadLeg");
const deadArmM = makeLiveThingM("deadArm");
const livingThings = deadLegM.ap(deadArmM).evalWith(1);
console.log(livingThings);
My problem is that it throws the following error:
/Users/pc/Soft/experiments/functional/crocks/node_modules/crocks/State/index.js:101
throw new TypeError('State.ap: Source value must be a function')
^
TypeError: State.ap: Source value must be a function
From what I see there, it's probably because I don't understand the apply function, or the way State.get is running. For me it accepts a function as its internal value in my code, but it doesn't seem so.
Can anybody explains and show me what I'm doing wrong here ?
Thanks for your help
Welcome to functional programming in JS and thank you for giving crocks a shot.
In looking at that article, one of the things to note is that the author is presenting how the mechanics work inside of the State ADT, and not really how to use an existing State ADT.
I will provide an explanation on how to handle the State transactions manually, which is close to what you have in your implementation. Then I will give a brief example of how the construction helpers (like get and modify could be used to decrement the VitalForce) are used to handle the and build State transactions.
Also I will give a brief explanation of using Applicatives.
So to start lets bring in a couple ADTs from crocks
const Pair = require('crocks/Pair')
const State = require('crocks/State')
We need the State constructor to take in a function that will return a Pair (the tuple that the author mentions in the post). How the construction function works can be found here.
Before we can discuss the State function, we need that LivingPart function:
// LivingPart :: (Integer, String) -> Object
const LivingPart = (unitOfForce, part) =>
({ [part]: { unitOfForce } })
I have changed the structure from what you originally had so we could merge any given Part together with another.
Now with that bit in the mix we can implement makeLiveThing. You pretty much had it in your implementation. The only real difference here is we need to construct the State ADT with the function and return the Pair. Notice that we still bring in the String, BUT return a State ADT that will be executed when runWith is called. Remember that the current state will be passed into the function that the State instance wraps (in this case, vitalForce is our state):
// makeLiveThing :: String -> State Integer Object
const makeLiveThing = part => State(
vitalForce => {
const unitOfForce = 1
const remaining = vitalForce - unitOfForce
return Pair(
LivingPart(unitOfForce, part),
remaining
)
}
)
Now that we have a means to create a LivingPart and handle our state transaction (decrementing by 1), we can create a couple parts:
// rightLeg :: State Integer Object
const rightLeg =
makeLiveThing('right-leg')
// leftArm :: State Integer Object
const leftArm =
makeLiveThing('left-arm')
Now comes the task of joining these State instances. You were right to think to use apply, as when an ADT has both an ap method and an of method it is called an Applicative. When we have an Applicative we can think of the type as being able to combine (2) independent instances that do not depend on the result of the other one. We just need to provide a way to tell the type how to combine it.
Typically that is done with a function that can act on the types contained in the ADT. In our case it is an Object, so one way to combine (2) objects is with Object.assign. crocks provides a helper called assign that can do just that, so lets bring it in:
const assign = require('crocks/helpers/assign')
Now that we have a way to combine the internal values, we need to "lift" this function into our State type, crocks also has a function that can be used on Applicatives to lift and apply the internal values of ADT (2) instances called liftA2. Which means "lift a function into an applicative with 2 instances"
So lets bring that in as well and then create a function that will be used to join (2) Parts:
const liftA2 = require('crocks/helpers/liftA2')
// joinParts :: Applicative m => m Object -> m Object -> m Object
const joinParts =
liftA2(assign)
Now with this function we can lift and join those parts and run the result with our VitalForce:
joinParts(rightLeg, leftArm)
.runWith(10)
//=> Pair( { left-arm: { unitOfForce: 1 }, right-leg: { unitOfForce: 1 } }, 8 )
Notice the result has the resultant in the left (the combined living parts) and the state in the right (the remaining VitalForce).
Here are some references to that above:
assign function
liftA2 function
State ADT
Pair ADT
egghead course on the State API
Now I am going to show a brief example of how we can set up a single State transaction for taking VitalForce from our pool. I am not going to explain here in detail, but you should be able to glean some information between this example and the State documentation:
const State = require('crocks/State')
const constant = require('crocks/combinators/constant')
const mapProps = require('crocks/helpers/mapProps')
const { modify } = State
// decrementBy :: Integer -> Integer -> Integer
const decrementBy =
x => y => y - x
// VitalForce :: { units: Integer }
// decUnitsBy :: Integer -> VitalForce -> VitalForce
const decUnitsBy = units =>
mapProps({ units: decrementBy(units) })
// getVitalForce :: Integer -> State VitalForce VitalForce
const getVitalForce = units =>
modify(decUnitsBy(units))
.map(constant({ units }))
getVitalForce(3)
.runWith({ units: 10 })
//=> Pair( { units: 3 }, { units: 7 } )
Here are some docs for those included functions:
constant function
mapProps function
So as a side note, I do a LiveCode broadcast on this channel, I am going to go over this blog post and talk about how to implement this in crocks, if that is something you would be interested in.
Hope this helps!!

Create Dictionary in Javascript / PHP

I'm trying to create a dictionary from a .txt file in the shape of a tree. On every line of the text file there's a word, I extract all those words in an array.
Now regarding the tree, Each node contains a letter, if it's the last letter of a word, it contains a definition, and each node have an array Children that contains letters from all others words starting the same way.
So I have nodes defined this way:
function Node(letter,definition,children) {
this.letter = letter,
this.definition = "",
this.children = []
};
I have an array Dictionary that will contain all the nodes. Every Node will be organized (so that we know 'a' is in Dictionary[0] and 'b' in Dictionary[1] and so on).
I defined some functions to help build the dictionary:
check if Dictionary contains the first letter of the word we have (c is the character, dictio is the dictionary array and ascii is the ascii-97 value of the character)
function checkChar(c,dictio,ascii){
if(dictio[ascii].letter == c ){
return true;
}
return false;
};
create a node with the given character
function createChar(c){
var noeud = {
letter: c,
def: '',
children: []
};
return noeud;
};
Add the character to the dictionary
function addChar(c,dictio,ascii){
dictio.children[ascii] = createChar(c);
};
And I'm having trouble on the biggest function: the main on that adds the word and calls all of these small functions I've written. Which I'm having trouble making.
I don't even know if what I'm doing is right or wrong, if anyone could point me to the right direction or suggest a method in javascript or php to do dictionary from a TXT file that would be great.
Ok...
so this is an example of txt file containing words
//words.txt
hello
world
foo
bar
word_dictionary.php for parsing txt file and has method for checking if a word exists in tree/dictionary
<?php
//word_dictionary.php
class Node{
private $letter;
private $definition = '';
private $children = array();
function __construct($letter){
$this->letter = $letter;
}
function hasChild($letter){
return array_key_exists($letter,$this->children);
}
function addChild($letter){
$this->children[$letter] = new Node($letter);
return $this->children[$letter];
}
function getChild($letter){
return $this->children[$letter];
}
function setDefinition($definition){
$this->definition = $definition;
}
function getDefinition(){
return $this->definition;
}
function hasDefinition(){
return (bool)$this->definition;
}
}
// method for getting a word definition from tree/dictionary.
// if word exists return definition, else return false
function getDefinition($word,$tree){
$node = $tree;
$length = strlen($word);
foreach(str_split($word) as $index => $letter){
if($node->hasChild($letter)){
$node = $node->getChild($letter);
}
else{ // word not exists
return false;
}
if(($index+1) == $length){ // means last letter in word
return ($node->hasDefinition()) ? $node->getDefinition() : false;
}
}
}
// Start build your tree/dictionary. This part is execute ONCE only for building tree.
$anchor = new Node('');
$handle = fopen('words.txt','r');
while(($word = fgets($handle))){
$word = rtrim($word);
$length = strlen($word);
$node = $anchor;
foreach(str_split($word) as $index => $letter){
if($node->hasChild($letter)){
$node = $node->getChild($letter);
}
else{
$node = $node->addChild($letter);
}
if(($index+1) == $length ){
//print 'definition for word: '.$word."\n";
$node->setDefinition('definition for world: '.$word);
}
}
}
//use this function when a user type a word that you want to check if exists and return the definition to user. this flow should be in AJAX request from client
print getDefinition('bar',$anchor)."\n";
hope it help a bit ;)
first of all, you're asking if you are going in the right direction. Well, i think you are. This may not be the best implementation of the year but still all the things you said are coherent with each other and it seems pretty solid.
I don't think giving you a direct solution to your question would be didactic since you're working with trees and it seems you don't have much experience with them.
But i can give you some hint and references. A very convenient way to implement your "biggest function :)" would be to use a recursive function which would call itself on each children.
I suggest you take a look a this wikipedia article. It shows example of trees that look a bit like yours and implement a full search algorithm that you could adapt to your needs without too much problem.
Hope the english wasn't that bad, and that it will help you

Prototype's Enumerable#pluck in F#?

In JavaScript, using the Prototype library, the following functional construction is possible:
var words = ["aqueous", "strength", "hated", "sesquicentennial", "area"];
words.pluck('length');
//-> [7, 8, 5, 16, 4]
Note that this example code is equivalent to
words.map( function(word) { return word.length; } );
I wondered if something similar is possible in F#:
let words = ["aqueous"; "strength"; "hated";"sesquicentennial"; "area"]
//val words: string list
List.pluck 'Length' words
//int list = [7; 8; 5; 16; 4]
without having to write:
List.map (fun (s:string) -> s.Length) words
This would seem quite useful to me because then you don't have to write functions for every property to access them.
I saw your request on the F# mailing list. Hope I can help.
You could use type extension and reflection to allow this. We simple extend the generic list type with the pluck function. Then we can use pluck() on any list. An unknown property will return a list with the error string as its only contents.
type Microsoft.FSharp.Collections.List<'a> with
member list.pluck property =
try
let prop = typeof<'a>.GetProperty property
[for elm in list -> prop.GetValue(elm, [| |])]
with e->
[box <| "Error: Property '" + property + "'" +
" not found on type '" + typeof<'a>.Name + "'"]
let a = ["aqueous"; "strength"; "hated"; "sesquicentennial"; "area"]
a.pluck "Length"
a.pluck "Unknown"
which produces the follow result in the interactive window:
> a.pluck "Length" ;;
val it : obj list = [7; 8; 5; 16; 4]
> a.pluck "Unknown";;
val it : obj list = ["Error: Property 'Unknown' not found on type 'String'"]
warm regards,
DannyAsher
>
>
>
>
>
NOTE: When using <pre> the angle brackets around <'a> didn't show though in the preview window it looked fine. The backtick didn't work for me. Had to resort you the colorized version which is all wrong. I don't think I'll post here again until FSharp syntax is fully supported.
Prototype's pluck takes advantage of that in Javascript object.method() is the same as object[method].
Unfortunately you can't call String.Length either because it's not a static method. You can however use:
#r "FSharp.PowerPack.dll"
open Microsoft.FSharp.Compatibility
words |> List.map String.length
http://research.microsoft.com/fsharp/manual/FSharp.PowerPack/Microsoft.FSharp.Compatibility.String.html
However, using Compatibility will probably make things more confusing to people looking at your code.

Categories

Resources