viernes, octubre 30, 2009
deseo
* mira hacia arriba *
* desea ver mas preguntas de python en la lista de python *
* sopla *
estas cosas funcionan?
sino me compre una torta y unas velas en vano.
al menos tengo torta... *
* not
sábado, octubre 17, 2009
Mapeo de busquedas de couchdb-lucene en couchapp
después de preguntar en el irc de couchapp me dijeron que couchapp mapea directorios y archivos a estructuras json, así que lo que antes era
{
"_id": "_design/search-docs",
"fulltext": {
"documents": {
"index": "function(doc) { /* algo */ }"
},
"repos": {
"index": "function(doc) { /* algo */ }"
}
"authors": {
"index": "function(doc) { /* algo */ }"
}
}
}
se convierte en una jerarquía de directorios en la raíz de mi couchapp como la siguiente
fulltext/
|-- authors
| `-- index.js
|-- documents
| `-- index.js
`-- repos
`-- index.js
_design/search-docs pasa a ser _design/fresita (porque ese es el nombre de mi aplicación). Podemos encontrar las búsquedas en el path
_fti/$app-name/$search
por ejemplo:
_fti/fresita/authors?q=Mariano
todo mas prolijo y manejable..
martes, octubre 13, 2009
calculadora en erlang parte (multiplicación división y modulo)
calc_lexer.xrl:
Definitions.
D = [0-9]
AOP = (\+|-)
MOP = (\*|/|%)
WS = ([\000-\s]|#.*)
Rules.
{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{MOP} : {token,{mul_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+ : {token,{float,TokenLine,list_to_float(TokenChars)}}.
{WS}+ : skip_token.
Erlang code.
----------------------------------------
nada del otro mundo, hacemos lo mismo que con los signos de adicion pero cambiando la expresion regular y el identificador.
ahora el parser:
calc_parser.yrl
Nonterminals
predicate.
Terminals
add_operator mul_operator integer float.
Rootsymbol predicate.
Left 300 add_operator.
Left 400 mul_operator.
predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> predicate mul_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> integer : unwrap('$1').
predicate -> float : unwrap('$1').
Erlang code.
unwrap({_,_,V}) -> V.
----------------------------------
de nuevo, agregamos el identificador del nuevo simbolo no terminal, especificamos la asociatividad de la nueva operacion (tiene precedencia sobre la adicion). Y agregamos la regla para que lo transforme en una estructura de datos.
por ultimo la calculadora en si
calc.erl
-module(calc).
-export([solve/1]).
solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).
matches(A) when is_number(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches({'*', A, B}) -> matches(A) * matches(B);
matches({'/', A, B}) -> matches(A) / matches(B);
matches({'%', A, B}) -> matches(A) rem matches(B);
matches(_) -> error.
----------------------------------------------
agregamos el matching de las nuevas operaciones.
para probar un poco el codigo de build_calc:test()
test() ->
0 = calc:solve("1 + 2 - 3"),
6 = calc:solve("1 + 2 + 3"),
-4 = calc:solve("1 - 2 - 3"),
0.0 = calc:solve("1.0 + 2 - 3"),
6.0 = calc:solve("1 + 2.0 + 3"),
-4.0 = calc:solve("1 - 2.0 - 3"),
3.1 = calc:solve("1.0 + 2.1"),
7 = calc:solve("1 + 2 * 3"),
10 = calc:solve("2 * 3 + 4"),
7.1 = calc:solve("1.1 + 2 * 3"),
10.2 = calc:solve("2 * 3.1 + 4"),
1 = calc:solve("11 % 2"),
2 = calc:solve("1 + 11 % 2"),
11 = calc:solve("11 % 2 + 10"),
ok.
y por ultimo lo probamos
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> c(build_calc).
{ok,build_calc}
2> build_calc:build().
Old inliner: threshold=0 functions=[{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1},
{yeccpars2_7_,1},
{yeccpars2_6_,1},
{yeccpars2_3_,1},
{yeccpars2_2_,1}]
ok
3> rl(calc).
ok
4> rl(calc_parser).
ok
5> rl(calc_lexer).
ok
6> build_calc:test().
ok
en la proxima agregamos expresiones anidadas (parentesis)
haciendo andar couchdb-lucene 0.4 en couchdb 0.10.0
principalmente seguí estas dos guías
http://github.com/rnewson/couchdb-lucene
http://wiki.fluidproject.org/pages/viewpage.action?pageId=6823331
pero siempre la generalidad termina dejando algunas cosas flotando que hacen que uno pelee (hasta que mira "watch tail /usr/local/var/log/couchdb/couch.log")
supongo que tienen instalado y andando couchdb 0.10.0 (va a ser el caso si usan ubuntu karmic :)
lo primero que hacemos es bajar la versión estable de couchdb-lucene (actualmente la 0.4)
wget http://cloud.github.com/downloads/rnewson/couchdb-lucene/couchdb-lucene-0.4-jar-with-dependencies.jar.gz
luego (como dice el README que uno no lee) desempacamos el jar.gz con unpack200, si lo desempacamos con file-roller java se queja de que es un jar corrupto.
unpack200 couchdb-lucene-0.4-jar-with-dependencies.jar.gz couchdb-lucene-0.4-jar-with-dependencies.jar
creamos un directorio donde lucene pueda trabajar tranquilo
sudo mkdir /usr/local/var/lib/lucene
copiamos el jar
sudo cp couchdb-lucene-0.4-jar-with-dependencies.jar /usr/local/var/lib/lucene
cambiamos el dueño y los permisos al directorio
chown -R couchdb:couchdb /usr/local/var/lib/lucene
chmod -R 0770 /usr/local/var/lib/lucene
configuramos couchdb para que use lucene como indexador de full text search
sudo vim /usr/local/etc/couchdb/local.ini
(mi archivo local.ini tiene lo siguiente)
; CouchDB Configuration Settings
; Custom settings should be made in this file. They will override settings
; in default.ini, but unlike changes made to default.ini, this file won't be
; overwritten on server upgrade.
[couchdb]
;max_document_size = 4294967296 ; bytes
[httpd]
;port = 5984
;bind_address = 127.0.0.1
[log]
;level = debug
[couch_httpd_auth]
;secret = replace this with a real secret
[external]
fti=/usr/bin/java -Dcouchdb.lucene.dir=/usr/local/var/lib/lucene -jar /usr/local/var/lib/lucene/couchdb-lucene-0.4-jar-with-dependencies.jar -search
[update_notification]
indexer=/usr/bin/java -Dcouchdb.lucene.dir=/usr/local/var/lib/lucene -jar /usr/local/var/lib/lucene/couchdb-lucene-0.4-jar-with-dependencies.jar -index
[couchdb]
os_process_timeout=60000 ; increase the timeout to 60 seconds.
[httpd_db_handlers]
_fti = {couch_httpd_external, handle_external_req, <<"fti">>}
; To create an admin account uncomment the '[admins]' section below and add a
; line in the format 'username = password'. When you next start CouchDB, it
; will change the password to a hash (so that your passwords don't linger
; around in plain-text files). You can add more admin accounts with more
; 'username = password' lines. Don't forget to restart CouchDB after
; changing this.
[admins]
marianoguerra = mysecretpassword
;admin = mysecretpassword
(no, ese no es mi password :)
reiniciamos couchdb (lo matamos y lo levantamos de nuevo, esto depende de como lo levantaste, si no sabes, hace "ps aux | grep couchdb" y mata todo lo que parezca sospechoso :D)
lo levantamos de nuevo (yo lo levanto así)
sudo -i -u couchdb couchdb -b
vamos a la interfaz de administracion:
firefox http://localhost:5984/_utils/
elegimos nuestra base de datos, elegimos crear nuevo documento, clickeamos la pestaña source arriba a la derecha y pegamos algo parecido a lo siguiente:
{
"_id": "_design/search-docs",
"fulltext": {
"by_title": {
"index": "function(doc) { var ret=new Document(); ret.add(doc.title); return ret }"
},
"by_content": {
"index": "function(doc) { var ret=new Document(); ret.add(doc.body); return ret }"
}
}
}
el _id cambia por el nombre que mas te guste, las vistas que están adentro de fulltext también cambian según por que quieras indexar, yo estoy indexando por titulo y contenido de unos documentos.
ahora agregamos algunos documentos que puedan ser indexados, para sacarnos la duda de que lucene esta andando mirar la salida de /usr/local/var/log/couchdb/couch.log no debería haber mensajes de error y "ls /usr/local/var/lib/lucene" nos tendia que mostrar varios archivos de nombres extravagantes.
para probar nuestra búsqueda luego de agregar algunos documentos hacemos una query (yo agregue un documento que decía ficticio por todos lados :D)
curl http://localhost:5984/fresita/_fti/search-docs/by_title?q=ficticio
lo cual me devolvió
{"q":"default:ficticio","etag":"1244bf34b28","view_sig":"7368cf7d6f68ec4c60f40c52303de534","skip":0,"limit":25,"total_rows":1,"search_duration":14,"fetch_duration":0,"rows":[{"id":"9ed8514725e1dca56c57471286f5f389","score":2.556901454925537}]}
lunes, octubre 12, 2009
sábado, octubre 10, 2009
calculadora en erlang parte 2 (agregando floats)
en calc_lexer.xrl
Definitions.
D = [0-9]
AOP = (\+|-)
WS = ([\000-\s]|#.*)
Rules.
{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+ : {token,{float,TokenLine,list_to_float(TokenChars)}}.
{WS}+ : skip_token.
Erlang code.
en calc_parser.yrl
Nonterminals
predicate.
Terminals
add_operator integer float.
Rootsymbol predicate.
Left 300 add_operator.
predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> integer : unwrap('$1').
predicate -> float : unwrap('$1').
Erlang code.
unwrap({_,_,V}) -> V.
en calc.erl
-module(calc).
-export([solve/1]).
solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).
matches(A) when is_number(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches(_) -> error.
para facilitar la construccion, les dejo el codigo que tengo en build_calc.erl
-module(build_calc).
-export([build/0, test/0]).
build() ->
leex:file(calc_lexer),
yecc:file(calc_parser),
compile:file(calc_lexer),
compile:file(calc_parser),
compile:file(calc),
ok.
viernes, octubre 09, 2009
escribiendo una calculadora en erlang parte uno
en una serie de posts (espero seguir escribiéndolos) voy a ir desde cero hasta un interprete interactivo que permita hacer cosas como estas (y mas)
< a = 10 + (2 * 3) # calculo totalmente innecesario
> 16
< b = a * 2
> 32
< a
> 16
< b
> 32
< a == b or b > a
> true
este post es la patada de inicio asi que vamos a ver la herramientas.
Como lenguaje de programación obviamente vamos a usar erlang (que es lo que quiero aprender :D).
para el análisis léxico vamos a usar leex y para el análisis sintáctico y generación del árbol vamos a usar yecc.
para tener erlang hagan algo similar a:
sudo aptitude install erlang
(adaptenlo a su distro)
yecc viene incluido en erlang así que andamos con suerte, a donde no andamos con suerte es con leex, que lo tendremos que bajar de aquí:
http://forum.trapexit.org/download.php?id=184&sid=9eba63c4011e517629e8d5a73e4e2ff5
pueden buscar si hay una versión mas nueva acá:
http://forum.trapexit.org/viewtopic.php?p=43924#43924
lo bajan, descomprimen y ejecutan make en el directorio, luego para poder usarlo en erlang lo que hice (quizás no muy bueno pero anda :P) es crear un directorio llamado /usr/lib/erlang/lib/leex-0.3 y poner adentro leex.beam y el directorio include.
ahora con todo instalado vamos a largarnos con nuestro primer intento (remito a la documentación de cada uno para darse una mejor idea de que hacen y como se usan).
vamos a definir un lenguaje que nos permita sumar y restar números enteros
creemos un directorio llamado match-1-add-sub-ints
y adentro creamos un archivo llamado calc_lexer.xrl con el siguiente contenido
Definitions.
D = [0-9]
AOP = (\+|-)
WS = ([\000-\s]|#.*)
Rules.
{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{WS}+ : skip_token.
Erlang code.
este archivo lo que hace es separar un string en partes (o tokens) que identifican distintos componentes de nuestro lenguaje. En el caso de un lenguaje para sumar números enteros lo que queremos dividir son los números, los signos + y - y descartar los espacios (y también todo lo que venga después de # ya que son comentarios)
en la sección de definiciones definimos las distintas expresiones regulares.
definimos una expresión regular para números enteros
D = [0-9]
una para los signos + y -
AOP = (\+|-)
y una para los blancos y los comentarios
WS = ([\000-\s]|#.*)
luego en la sección Rules. definimos que debe generar (la parte de la derecha) cuando se encuentre con las expresiones de la izquierda.
si se encuentra con un signo + o - que genere una tupla que dice que es un operador de suma, que ponga la posición en donde encontró el token y que convierta lo que matchea la expresión a un atomo.
{AOP} : {token,{add_operator,TokenLine,list_to_atom(TokenChars)}}.
si se encuentra con uno o mas dígitos que genere una tupla que dice que es un entero, la posición del token y el numero entero que encontró.
{D}+ : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
si encuentra blancos que los salte.
{WS}+ : skip_token.
ahora ya podemos dividir el texto de entrada en pedazos mas manejables gracias a leex, ahora vamos a ver como salio esto. Iniciamos un interprete de erlang y le decimos a leex que genere un modulo de erlang para manejar los tokens que definimos, luego compilamos el modulo generado y lo probamos con un string valido
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> leex:file(calc_lexer).
{ok,"calc_lexer.erl"}
2> c(calc_lexer).
{ok,calc_lexer}
3> {ok, Tokens, _Endline} = calc_lexer:string("1 + 2 - 3 # esto es un comentario").
{ok,[{integer,1,1},
{add_operator,1,'+'},
{integer,1,2},
{add_operator,1,'-'},
{integer,1,3}],
1}
como vemos nos devolvió una estructura de datos con las partes que definimos.
ahora vamos a definir un modulo para poder hacer algo con ese árbol que definimos.
creamos un archivo que se llame calc_parser.yrl con el siguiente contenido:
Nonterminals
predicate.
Terminals
add_operator integer.
Rootsymbol predicate.
Left 300 add_operator.
predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
predicate -> integer : unwrap('$1').
Erlang code.
unwrap({_,_,V}) -> V.
este documento define los nodos no terminales, los nodos terminales el símbolo raíz, la asociatividad de los operandos y luego define la sintaxis (para mas datos sobre las primeras secciones leer la documentación en linea de yecc o algun libro sobre lenguajes, otra opción es ponerse a romper esto y aprender :D).
la parte mas interesante es la que define que un predicado puede estar compuesto por la suma de dos predicados:
predicate -> predicate add_operator predicate : {unwrap('$2'), '$1', '$3'}.
o puede ser solo un entero
predicate -> integer : unwrap('$1').
lo que esta después de los dos puntos es el código erlang ejecutado cuando esa expresión es encontrada. Lo que hacemos es generar otra estructura de datos que vamos a manipular en erlang.
luego tenemos una función de utilidad en erlang para sacar un valor de interés de una tupla.
ahora generamos el modulo erlang que va a parsear la salida de leex y convertirla en otra estructura:
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> yecc:file(calc_parser).
{ok,"calc_parser.erl"}
2> c(calc_parser).
{ok,calc_parser}
3> {ok, Tokens, _Endline} = calc_lexer:string("1 + 2 - 3").
{ok,[{integer,1,1},
{add_operator,1,'+'},
{integer,1,2},
{add_operator,1,'-'},
{integer,1,3}],
1}
4> {ok, Tree} = calc_parser:parse(Tokens)
4> .
{ok,{'-',{'+',1,2},3}}
para los curiosos, si se fijan el resultado que nos dio es muy parecido a una expresión de lisp :)
{'-',{'+',1,2},3}
[1]> (- (+ 1 2) 3)
0
bueno, ahora tenemos algo que se parece mucho a lisp, podríamos escribir lisp! pero no :D vamos a interpretarlo con un programa de erlang.
creen un archivo calc.erl que contenga lo siguiente:
-module(calc).
-export([solve/1]).
solve(String) ->
{ok, Tokens, _Endline} = calc_lexer:string(String),
{ok, Tree} = calc_parser:parse(Tokens),
matches(Tree).
matches(A) when is_integer(A) -> A;
matches({'+', A, B}) -> matches(A) + matches(B);
matches({'-', A, B}) -> matches(A) - matches(B);
matches(_) -> error.
lo que hace este modulo es definir una función que recibe un string por parámetro con el calculo que queremos realizar, lo pasar por el analizador lexicografico, le da la salida de este como entrada al parser y con el árbol sintáctico resultante llama a la función matches que gracias al pattern matching se encarga de ejecutar recursivamente la instrucción correcta y devolver el resultado final (o un error en caso de que algo no matchee).
ahora vamos a compilar esto y probarlo un poco:
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> c(calc).
{ok,calc}
2> calc:solve("1 + 2 - 3 # deberia dar 0").
0
3> calc:solve("1 + 2 - 3 - 4").
-4
bueno, ya tenemos la primera versión de nuestra calculadora, en la próxima vamos a agregar soporte para números de punto flotante con algunos leves cambios.
para los fiacosos como yo, el codigo de este ejemplo (y de los subsiguientes) esta en github aca:
http://github.com/marianoguerra/match
sábado, septiembre 26, 2009
mi nuevo proyecto
el proyecto se llama tubes y esta hosteado (usando tubes :P) acá:
http://a-series-of-tubes.appspot.com/
los invito a probarlo, divertirse un rato, hacer preguntas del tipo "porque otro framework web en python???" etc etc.
viernes, septiembre 11, 2009
Pubsubhubbub developer's guide
naming conventions
- pshb/PSHB = pubsubhubbub
- gae/GAE = Google App Engine
- things that start with "$" are bash commands
- things that start with "!" are important notes
create directory to hold all the content of this tutorial
$ mkdir pshb
$ cd pshb
download Google App Engine SDK
$ wget http://googleappengine.googlecode.com/files/google_appengine_1.2.4.zip
$ unzip google_appengine_1.2.4.zip
download the latest version of pshb
$ svn checkout http://pubsubhubbub.googlecode.com/svn/trunk/ pubsubhubbub
start pshb
$ python2.5 google_appengine/dev_appserver.py pubsubhubbub/hub/
check that the hub started
type http://localhost:8080 on your browser, you should see something like:
Welcome to the demo PubSubHubbub reference Hub server!

download the example
# we will need to fetch the tubes library and the example that we will be using.
$ git clone git://github.com/marianoguerra/tubes.git
# now we will start the example
$ cd tubes/ihasfriendz/
$ python main.py
* Running on http://0.0.0.0:8081/
* Restarting with reloader...
components used by the example
- werkzeug: http://werkzeug.pocoo.org
- jquery: http://jquery.com
- tubes: http://github.com/marianoguerra/tubes/tree/master
- pubsubhubbub_publish.py: from pshb
- feedformatter.py: http://code.google.com/p/feedformatter/
publish the feed to the hub
go to the following URL on your browser: http://localhost:8080/publish
on the Topic field enter: http://localhost:8081/atom/stream/_MYUSER_ and click publish

subscribing to the hub
go to the following URL on your browser: http://localhost:8080/subscribe

create some content
go to the following URL on your browser: http://localhost:8081/files/index.html
post some content on the form, use the user you used as MYUSER

you can check that the item was posted going manually to http://localhost:8081/atom/stream/_MYUSER_, you should see an atom feed there
manually processing the tasks

click on the feed-pulls link, there click on the run button.
when we run the feed pulls task, we tell pshb to fetch the feeds that have new content (the ones that did a post to the hub to inform that there is new content)

! on production hubs this tasks are done automatically
now we go again to the Task Queues page, there the event-delivery queue should have a number different than 0 (the number of messages that are pending to be sent to the subscribers), we click on the event-delivery and then we click on the run button.

seeing it work

for lazy people
#!/usr/bin/env sh
# create the example directory
mkdir pshb
cd pshb
CWD=$(pwd)
EXAMPLE=$CWD/tubes/ihasfriendz
wget http://googleappengine.googlecode.com/files/google_appengine_1.2.4.zip
unzip google_appengine_1.2.4.zip
svn checkout http://pubsubhubbub.googlecode.com/svn/trunk/ pubsubhubbub
# we will need to fetch the tubes library and the example that we will be using.
git clone git://github.com/marianoguerra/tubes.git
# now we will start the example
echo "run \"cd $EXAMPLE; python main.py\" on a shell to run the example"
echo "run \"cd $PWD; python2.5 google_appengine/dev_appserver.py pubsubhubbub/hub/\" on a shell to run the hub"
jueves, septiembre 10, 2009
playing with mercurial API
[ -d fresita ] && rm -rf fresita
mkdir fresita
cd fresita
echo "test" > README.txt
my first test script:
import os
from mercurial import ui
from mercurial import commands
from mercurial import localrepo
ui_ = ui.ui()
create = 1
path = os.path.abspath('./fresita')
def repo_initialized(path):
'''return True if the repo is already initialized'''
return os.path.isdir(os.path.join(path, '.hg'))
if repo_initialized(path):
create = 0
repo = localrepo.localrepository(ui_, path, create)
repo.add(['README.txt'])
modified, added, removed, deleted, unknown, ignored, clean = repo.status()
print 'modified', modified
print 'added', added
print 'removed', removed
print 'deleted', deleted
print 'unknown', unknown
print 'ignored', ignored
print 'clean', clean
repo.commit(text="my first commit")
pretty easy :)
domingo, agosto 30, 2009
Portishead - Magic Doors
http://www.youtube.com/watch?v=yRZlBNaFYqk
La banda fue formada en 1991 por el teclista Geoff Barrow y la cantante Beth Gibbons que tomaron el nombre de la localidad natal de Barrow, situada a 15 km al oeste de Bristol. Barrow había trabajado anteriormente con otras dos bandas de trip hop de Bristol, Massive Attack y Tricky.
Después de estrenar el cortometraje To Kill a Dead Man, Portishead firmó un contrato con la discográfica Go! Beat que editó su primer álbum Dummy en 1994, con la colaboración del guitarrista Adrian Utley. A pesar de la nula campaña de publicidad de la banda, el álbum fue un éxito en Estados Unidos y Europa, los sencillos "Glory Box" y "Sour Times" alcanzaron la cabeza de las listas de éxitos.
Su segundo álbum Portishead, salió a la venta en 1997 e incluyó el sencillo "All Mine". Además, grabaron un concierto con acompañamiento de orquesta en Roseland, Nueva York, donde fueron interpretadas sus canciones más famosas. Éste, salió a la venta en 1998 con un DVD del concierto. En 1999 colaboraron con Tom Jones en una canción de su álbum Reload.
Portishead y otros grupos de Trip hop no están conformes con este término, argumentando que es sólo una invención mediática para catalogar su música inclasificable.
El 24 de enero de 2008 Portishead anunció la salida de un nuevo disco, titulado Third, programado para el 14 de abril del mismo año.http://en.wikipedia.org/wiki/Third_(Portishead_album)
viernes, agosto 21, 2009
politica de cacheo de requests GET de IE
now = time.time()
handler.set_header("Expires", now)
handler.set_header("Last-Modified", now)
handler.set_header("Date", now)
(tradúzcanlo al framework/librería que usen)
PD: hasta ahora parece andar pero le dejo la puerta entreabierta a IE para que me sorprenda...
miércoles, agosto 05, 2009
Gente grosa
travis pastrana, no puede tener tantos huevos
si hablamos de tecnica pura: rodney mullen
este truco es epico:
el primer best trick que me dejo de cara, el famoso 900 de tony hawk:
matt hoffmann no se queda atras y lo hace no handed en bicicleta
y por ultimo un poco de mi deporte preferido (que hace mucho que no practico :S)
jueves, julio 30, 2009
analizando interfaces de usuario inconscientemente
Es la primera vez que saco plata en esos cajeros así que no se si todos son iguales pero automáticamente me puse a analizar la interfaz de usuario, me llamo la atención de que si pones 4 números y están bien pasa derecho a la próxima pantalla, no se que hará si los números están mal, habría que probar si poniendo números mal en 5 segundos no hace nada podemos probar muchas combinaciones hasta que pase (no probado).
Pero eso es un detalle nomas, la pantalla de inicio me dio aproximadamente 8 opciones con variaciones mínimas que podrían haber sido movidas a otro submenu, la opción de extracción te pregunta de donde lo queres sacar y cuenta corriente en pesos no esta en ningún extremo si no en el centro a la derecha, los otros diálogos me parecieron también muy llenos de opciones y las opciones que generalmente son las por defecto no están en un lugar constante y visible (por ejemplo siempre arriba a la izquierda o similar).
Por ultimo el cajero te da la plata y el ticket y después te pregunta si queres hacer otra operación o finalizar. Razón por la cual casi me olvido la tarjeta ya que saque la plata, el ticket y como en los otros cajeros la tarjeta te la da antes con una chicharra y una luz es menos factible que te la olvides.
Si soy un colgado olvidador de cosas y pienso giladas pero me di cuenta como el cajero al que voy me simplifica la vida sin que me de cuenta :D.
miércoles, julio 29, 2009
correcciones de problemas de MPI
plus un arreglo en la declaracion de la estructura en el segundo programa.
#include <stdio.h>
#include <mpi.h>
int main (int argc, char** argv) {
int rank, size, value, i;
MPI_Status status;
MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
do {
if(rank == 0) {
printf("give me a value: ");
scanf("%d", &value);
}
MPI_Bcast(&value, 1, MPI_INT, 0, MPI_COMM_WORLD);
printf("process %d received %d\n", rank, value);
fflush(stdout);
} while(value >= 0);
printf("negative value, closing\n");
MPI_Finalize();
return 0;
}
#include <stdio.h>
#include <mpi.h>
MPI_Datatype IntDoubleType = 0;
typedef struct {
int value_int;
double value_double;
}IntDouble;
MPI_Datatype create_int_double_type() {
if(IntDoubleType == 0) {
MPI_Aint array_of_displacements[2];
MPI_Datatype array_of_types[2];
int array_of_blocklengths[2];
array_of_displacements[0] = 0;
array_of_types[0] = MPI_INT;
array_of_blocklengths[0] = sizeof(int);
array_of_displacements[1] = sizeof(int);
array_of_types[1] = MPI_DOUBLE;
array_of_blocklengths[1] = sizeof(double);
MPI_Type_struct(1, array_of_blocklengths, array_of_displacements,
array_of_types, &IntDoubleType);
MPI_Type_commit(&IntDoubleType);
}
return IntDoubleType;
}
int main (int argc, char** argv) {
int rank, size, value_int, i;
double value_double;
IntDouble value;
MPI_Status status;
MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
create_int_double_type();
do {
if(rank == 0) {
printf("give me an int value: ");
scanf("%d", &value_int);
printf("give me a double value: ");
scanf("%lf", &value_double);
value.value_int = value_int;
value.value_double = value_double;
}
MPI_Bcast(&value, 1, IntDoubleType, 0, MPI_COMM_WORLD);
printf("process %d received %d %lf\n", rank, value.value_int,
value.value_double);
fflush(stdout);
} while(value.value_int >= 0);
printf("negative value, closing\n");
MPI_Finalize();
return 0;
}
sábado, julio 25, 2009
generando funciones dinamicamente (a.k.a generando bytecode a mano)
def fun(arg0, arg1, arg2, arg3): pass
y no
def fun(*args): pass
ya que la API de dbus usa inspect para determinar el numero de argumentos posicionales
me tomo varias horas sacarlo, buscaba y buscaba y cada vez iba mas a bajo nivel. Hasta que llegue al mas bajo nivel que se puede llegar... generar bytecodes :)
y bue, pelee con eso un buen rato ya que si bien alguna vez jugue haciendo maquinas virtuales de juguete esta era la primera vez contra una en serio.
el resultado es el siguiente:
import dis
import types
def gen(num):
op = dis.opmap.__getitem__
ops = [op('LOAD_GLOBAL'), 0, 0]
for argnum in range(num):
ops.append(op('LOAD_FAST'))
ops.append(argnum)
ops.append(0)
ops.append(op('CALL_FUNCTION'))
ops.append(num)
ops.append(0)
ops.append(op('RETURN_VALUE'))
code_str = ''.join(chr(x) for x in ops)
return code_str
def gen_code(num, global_vars, name='func', filename='magic.py'):
code = gen(num)
varnames = ['arg' + str(x) for x in range(num)]
names = global_vars + varnames
names = tuple(names)
varnames = tuple(varnames)
return types.CodeType(num, num, num, 0, code, (), names, varnames, filename, name, 1, '')
def gen_fun(num, name, func):
code = gen_code(num, [func.__name__], name)
return types.FunctionType(code,{func.__name__: func})
if __name__ == '__main__':
def printer(*args):
print args
f4 = gen_fun(4, 'f4', printer)
dis.dis(f4.func_code)
f4(1,2,3,4)
f1 = gen_fun(1, 'f1', printer)
dis.dis(f1.func_code)
f1('only one arg')
try:
f1(1, 2)
except TypeError:
print 'ok, ok, only one argument'
lo que hace es generar una funcion que recibe N parametros y que lo unico que hace es llamar a otra funcion pasandole esos parametros, lo cual seria algo como:
def fun1(*args): print args
def fun_que_recibe_4_parametros(a,b,c,d): fun1(a,b,c,d)
con lo que si escribimos nuestra funcion en fun1 y creamos las funciones que reciben los distintos parametros con un for tenemos lo que necesitamos :)
la salida de la ejecusion de lo de arriba es:
1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 LOAD_FAST 1 (arg1)
9 LOAD_FAST 2 (arg2)
12 LOAD_FAST 3 (arg3)
15 CALL_FUNCTION 4
18 RETURN_VALUE
(1, 2, 3, 4)
1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 CALL_FUNCTION 1
9 RETURN_VALUE
('only one arg',)
ok, ok, only one argument
fuentes de inspiracion:
http://pyref.infogami.com/type-code
http://docs.python.org/library/dis.html
http://docs.python.org/library/types
http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy (la parte de callable types)
lunes, julio 20, 2009
Resolviendo ejercicios de MPI (y tambien en erlang) Parte 4
http://www.mcs.anl.gov/research/projects/mpi/tutorial/mpiexmpl/src/ring/C/main.html
Write a program that takes data from process zero and sends it to all of the other processes by sending it in a ring. That is, process i should receive the data and send it to process i+1, until the last process is reached.
la solucion en C salio bastante facil haciendo copypasta de los problemas anteriores y sorprendentemente en este caso los otros procesos si imprimieron a la consola:
#include <stdio.h>
#include <mpi.h>
int main (int argc, char** argv) {
int rank, size, value, i;
MPI_Status status;
MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
do {
if(rank == 0) {
printf("give me a value: ");
scanf("%d", &value);
fflush(stdout);
MPI_Send(&value, 1, MPI_INT, rank + 1, 1, MPI_COMM_WORLD);
}
else{
MPI_Recv(&value, 1, MPI_INT, rank - 1, 1, MPI_COMM_WORLD, &status);
printf("process %d received %d\n", rank, value);
if(rank + 1 < size) {
printf("sending to %d\n", rank + 1, value);
MPI_Send(&value, 1, MPI_INT, rank + 1, 1, MPI_COMM_WORLD);
}
fflush(stdout);
}
} while(value >= 0);
printf("negative value, closing\n");
MPI_Finalize();
return 0;
}
la solucion en erlang (tambien robando del ejercicio anterior):
-module(ej5).
-export([run/1]).
run(Total) ->
NextPid = spawn_listeners(Total),
get_values(NextPid).
spawn_listeners(Count) -> spawn_listeners(Count, none).
spawn_listeners(0, NextPid) -> NextPid;
spawn_listeners(Count, LastPid) ->
NextPid = spawn(fun() -> listener(LastPid) end),
spawn_listeners(Count - 1, NextPid).
listener(NextPid) ->
receive
Int ->
io:format("process ~p received ~p~n", [self(), Int]),
if
NextPid /= none ->
io:format("sending to ~p~n", [NextPid]),
NextPid ! Int;
true -> ok
end,
if
Int >= 0 -> listener(NextPid);
true ->
io:format("negative value, closing ~p~n", [self()])
end
end.
get_value(Message, ConversionFun) ->
case io:get_line(Message) of
{error, Reason} -> {error, Reason};
eof -> {error, eof};
Value -> ConversionFun(Value)
end.
get_values(NextPid) ->
case get_value("give me an int value (negative to quit): ", fun(Val) -> string:to_integer(Val) end) of
{error, Reason} ->
io:format("error reading int value (~p)~n", [Reason]),
get_values(NextPid);
{IntValue, _Rest} ->
io:format("sending to ~p~n", [NextPid]),
NextPid ! IntValue,
if
IntValue >= 0 -> get_values(NextPid);
true -> io:format("negative value, closing~n")
end
end.
ejemplo de salida:
$ erl
Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false]
Eshell V5.6.5 (abort with ^G)
1> c(ej5).
{ok,ej5}
2> ej5:run(3).
give me an int value (negative to quit): 3
sending to <0.40.0>
process <0.40.0> received 3
sending to <0.39.0>
process <0.39.0> received 3
sending to <0.38.0>
process <0.38.0> received 3
give me an int value (negative to quit): asd
error reading int value (no_integer)
give me an int value (negative to quit): 42
sending to <0.40.0>
process <0.40.0> received 42
sending to <0.39.0>
process <0.39.0> received 42
sending to <0.38.0>
process <0.38.0> received 42
give me an int value (negative to quit): -1
sending to <0.40.0>
negative value, closing
process <0.40.0> received -1
sending to <0.39.0>
ok
negative value, closing <0.40.0>
process <0.39.0> received -1
sending to <0.38.0>
negative value, closing <0.39.0>
process <0.38.0> received -1
negative value, closing <0.38.0>
domingo, julio 19, 2009
Resolviendo ejercicios de MPI (y tambien en erlang) Parte 3
In this assignment, you will modify your arguement broadcast routine to communicate different datatypes with a single MPI broadcast (MPI_Bcast) call. Have your program read an integer and a double-precision value from standard input (from process 0, as before), and communicate this to all of the other processes with an MPI_Bcast call. Use MPI datatypes.
Have all processes exit when a negative integer is read.
aca esta la resolucion en C:
#include <stdio.h>
#include <mpi.h>
MPI_Datatype IntDoubleType = 0;
typedef struct {
int value_int;
double value_double;
}IntDouble;
MPI_Datatype create_int_double_type() {
if(IntDoubleType == 0) {
MPI_Aint array_of_displacements[2];
MPI_Datatype array_of_types[2];
int array_of_blocklengths[2];
array_of_displacements[0] = 0;
array_of_types[0] = MPI_INT;
array_of_blocklengths[0] = 1;
array_of_displacements[1] = 1;
array_of_types[1] = MPI_DOUBLE;
array_of_blocklengths[1] = 1;
MPI_Type_struct(1, array_of_blocklengths, array_of_displacements,
array_of_types, &IntDoubleType);
MPI_Type_commit(&IntDoubleType);
}
return IntDoubleType;
}
int main (int argc, char** argv) {
int rank, size, value_int, i;
double value_double;
IntDouble value;
MPI_Status status;
MPI_Init (&argc, &argv); /* starts MPI */
MPI_Comm_rank (MPI_COMM_WORLD, &rank); /* get current process id */
MPI_Comm_size (MPI_COMM_WORLD, &size); /* get number of processes */
create_int_double_type();
printf("process %d of %d\n", rank, size);
do {
if(rank == 0) {
printf("give me an int value: ");
scanf("%d", &value_int);
printf("give me a double value: ");
scanf("%lf", &value_double);
value.value_int = value_int;
value.value_double = value_double;
MPI_Bcast(&value, 1, IntDoubleType, 0, MPI_COMM_WORLD);
}
else {
MPI_Recv(&value, 1, IntDoubleType, 0, 1, MPI_COMM_WORLD, &status);
printf("process %d received %d %lf\n", rank, value.value_int,
value.value_double);
fflush(stdout);
}
} while(value.value_int >= 0);
printf("negative value, closing\n");
MPI_Finalize();
return 0;
}
aca la resolucion en erlang:
-module(ej3).
-export([run/1]).
run(Total) ->
Pids = spawn_listeners(Total),
get_values(Pids).
spawn_listeners(Count) -> spawn_listeners(Count, []).
spawn_listeners(0, Pids) -> Pids;
spawn_listeners(Count, Pids) ->
Pid = spawn(fun() -> listener() end),
spawn_listeners(Count - 1, [Pid|Pids]).
listener() ->
receive
{Int, Double} ->
io:format("process ~p received ~p ~p~n", [self(), Int, Double]),
if
Int >= 0 -> listener();
true ->
io:format("negative value, closing ~p~n", [self()])
end
end.
get_value(Message, ConversionFun) ->
case io:get_line(Message) of
{error, Reason} -> {error, Reason};
eof -> {error, eof};
Value -> ConversionFun(Value)
end.
get_values(Pids) ->
case get_value("give me an int value (negative to quit): ", fun(Val) -> string:to_integer(Val) end) of
{error, Reason} ->
io:format("error reading int value (~p)~n", [Reason]),
get_values(Pids);
{IntValue, _Rest} ->
case get_value("give me a double value): ", fun(Val) -> string:to_float(Val) end) of
{error, Reason} ->
io:format("error reading double value (~p)~n", [Reason]),
get_values(Pids);
{DoubleValue, _Rest} ->
send_values({IntValue, DoubleValue}, Pids),
if
IntValue >= 0 -> get_values(Pids);
true -> io:format("negative value, closing~n")
end
end
end.
send_values(Value, Pids) ->
lists:foreach(fun(Pid) -> Pid ! Value end, Pids).
Algunas observaciones.
Cuando el problema empieza a escalar en complejidad de los tipos de datos enviados vemos que erlang empieza a hacernos las tareas mas faciles sin tener que declarar tipos complejos antes de enviarlos.
Otra cosa que note es que erlang un poco nos obliga a tratar los errores con el tema de pattern matching por lo que si bien el codigo erlang es un poco mas largo en realidad esta teniendo en cuenta todos los posibles casos de fallo, en el de C por ejemplo si no entro un entero o un float, el comportamiento es indeterminado (en mi maquina se mete en un loop infinito haciendo printf). Ya se que deberia usar algo como un buffer de string y despues usar atoi o similar pero para algo esta scanf :D.
Seguidores
Archivo del Blog
-
►
2011
(74)
- ► septiembre (4)
-
►
2010
(111)
- ► septiembre (8)
-
►
2009
(96)
- ► septiembre (3)
-
►
2008
(60)
- ► septiembre (8)
-
►
2007
(64)
- ► septiembre (1)
-
►
2006
(81)
- ► septiembre (1)
