lunes, enero 29, 2007

Exceso de información, filtros bayesianos y DIY

Desde que leí esta entrada de manje, "Selección de noticias mediante filtro bayesiano" en la que presentaba su proyecto, lo pensé... ¿por que nadie ha hecho un lector al que lo entrenes y te filtre según lo has entrenado? Bueno, manje lo tiene hecho y se pude probar (es TU PERIODICO)

No estoy seguro de que un filtrado automático sea la solución al exceso de fuentes de información, además de tenerlo que entrenar, con la inversión en esfuerzo que eso supone. Pero a este respecto manje y los usuarios de TU PERIODICO nos pueden comentar más. Yo a nivel personal si que digo que me ha resultado más entretenido programar un miniscrí que entrenar el filtro y que sigo usando bloglines a pesar de haber alternativas seguramente mejores...

En fin, como me interesó la idea, al menos un rato :), pues me puse a hacerlo yo mismo... porqué no, ¿verdad? Total que me hice un script en Ruby, porque me apetecía, porque yo lo valgo y para ser buzzword compliant :) (No sé, a otros les ha servido de algo :)). Como no tengo ningún tiempo de seguir su desarrollo (y tampoco se para que...) lo pongo por aquí por si le sirve a alguien, aunque sea solo de mal ejemplo. Aviso que está hecho con la velocidad y el descuido de la pasión, con lo que no es de calidad de producción ni en la forma ni en la organización.

Permite leer feed por feed en modo interactivo o todo seguido y se va guardando las fechas de última lectura de las fuentes, con lo que en teoría debería mostrar solo los ítems nuevos. En la práctica, sospecho que a mitad por el parser de feeds que he usado, Ruby feedparser, a mitad por que los que los forman no tienen mucho cuidado (pero no lo he analizado en serio) hay fuentes que salen entradas ya leídas. El escrí permite ir llenando su "base de datos" de fuentes o importarlas de bloglines. Permite, claro, entrenarlo (con Classifier, que se instala a través de rubygems) y permite que la salida sea en modo texto, para esos frikis como yo a los que nos encanta la consolica, y claro, para todos aquellos que quieran procesar la salida...

Después de estos preámbulos, ahí va:


require 'rubygems'
require 'stemmer'
require 'classifier'
require 'yaml'

class SimpleFeed
attr_reader :url, :date
attr_writer :date
def initialize(url, date)
@url = url
@date = date
end
end

def serialize( c, name )
File.open( name, 'w' ) do |out|
YAML.dump( c, out )
end
end

require 'net/http'
require 'uri'

def fetch(uri_str, date, limit = 10)
#TODO: I should choose better exception.
raise ArgumentError, 'HTTP redirect too deep' if limit == 0

header={'If-Modified-Since' => date.to_s}
uri=URI.parse(uri_str)
http = Net::HTTP.new(uri.host, uri.port)
response=http.get(uri.path,header)
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
else
puts "error getting " + uri_str + response
STDIN.gets()
response.error!
end
end

#const
Filter_file = "filter.yaml"
Feeds_file = "feeds.yaml"


#default options
add_feed = false
train = false
html_content = false
no_interactive = false
bloglines_import = false
bloglines_file = ''


ARGV.each_with_index do |arg,index|
case arg
when '--add_feed'
add_feed = true
when '--train'
train = true
when '--html_content'
html_content = true
when '--no_interactive'
no_interactive = true
when '--bloglines_import'
bloglines_import = true
bloglines_file = ARGV[index+1]
puts bloglines_file
end

end

#load filter
begin
filter =YAML::load_file( Filter_file )
rescue SystemCallError
filter= Classifier::Bayes.new 'Interesting', 'Uninteresting'
end


#load feeds
feeds = Array.new
if(bloglines_import == false)
begin
feeds = YAML::load_file(Feeds_file)
rescue SystemCallError
end
else
require 'rexml/document'
include REXML
file = File.new(bloglines_file)
doc = Document.new(file)
doc.root.each_element('body/outline/outline'){
|outline| feeds.push(SimpleFeed.new(outline.attributes['xmlUrl'],Time.at(0)))
}
end

if(add_feed == true)
print ('Insert a feed, please: ')
f=SimpleFeed.new(STDIN.gets().chop!,Time.at(0))
feeds.push(f)
end


require 'feedparser'
require 'feedparser/html2text-parser'

#iterate the feeds
feeds.each{ |sf|
puts 'Loading '+sf.url + ' ...'
begin
res=fetch(sf.url,sf.date)
s= res.body
rescue
puts "Error getting "+ sf.url
end

begin
#puts res['Date']
mod_date = res['Last-Modified']
puts mod_date
#URI::parse(sf.url), header
rescue
puts res
puts "Error: Web server doesn't implement date: "+ sf.url
end

begin
f = FeedParser::Feed::new(s)
if( f != nil and f.title!=nil )
puts(f.title)
else
puts("Title not available")
end
rescue
puts "Error parsing feed, Not valid: " + sf.url + "\n" +s
end
#Feeds are usually in temporal reverse order
if(f!=nil and f.items!=nil)
f.items.reverse_each {
|i|
#title, date, content
begin
if((i.date ==nil or sf.date==nil or i.date>sf.date) )
if(html_content == false and i.content != nil)

p = FeedParser::HTML2TextParser::new(true)
p.feed(i.content)
p.close
i.content = p.savedata
end
#TODO: -> content NULL
entry = ''
if i.title
entry+=i.title + " "# + i.date.to_s
end
if(i.content != nil)
entry+=i.content
end
puts(entry)
#puts ("Most recent date: " +sf.url+ " "+ i.date.to_s)
if(i.date!=nil)
sf.date=i.date
else
if (mod_date !=nil)
#If mod_date exists
sf.date=mod_date
end
end

if(train)
print(' (Interesting[Y/n]?)')
ans=STDIN.gets().chop!
print(ans)
if ans=='n'
filter.train_uninteresting(entry)
else
filter.train_interesting(entry)
end
else

puts(filter.classify(entry))# returns '[Un]interesting'
if(no_interactive==false)
STDIN.gets()
end
end
end
rescue
puts "Unknown Error"
end

}
end
}
serialize(filter, Filter_file)
serialize(feeds, Feeds_file)



La misma entrada en BP

lunes, diciembre 18, 2006

Memoria transaccional y concurrencia

En ACM Queue han publicado un articulo que ilustra bastante bien por donde irán los tiros (seguramente) en esto de la programación concurrente. Se trata de Unlocking Concurrency: Multicore programming with transactional memory y describe un poco que es la memoria transaccional y como se programaría en un lenguaje de los de uso cotidiano.

Básicamente se trata de especificar en el lenguaje que secuencias de operaciones forman una transacción de memoria, en la cual, como su nombre indica, o bien se realiza la operación completamente o bien es como si no se hubiese operado con ella, evitando la sincronización manual por parte del programador. Como se encargan de subrayar en el artículo tampoco es la panacea, pero ayuda(ría) mucho a la gestión de la concurrencia.

Sólo falta ahora que se implemente en lenguajes de uso común y deje de ser sólo objeto de investigación académica...


La misma entrada en BP

jueves, diciembre 14, 2006

Una herramienta, no una religión

Entrada interesante en Adding Simplicity - An Engineering Mantra, It's a Tool, Not a Religion!. Traduciendo muy libremente:

Recientemente he estado asistiendo al debate de REST contra SOA. La última semana me vi envuelto en discusiones de C++ contra Java. Esta noche pasada me tropecé con algunos debates sobre Django contra Rails. Me parece que los ingenieros de software somos únicos en el mundo técnico: más que disfrutar con una caja de herramientas variada, discutimos contra su contenido, esperando descartar todas excepto el conjunto más pequeño posible.

[...]

¿Porque debemos trabajar tanto en reducir nuestro conjunto de herramientas hasta la herramienta definitiva? Hay una tendencia a buscar el lenguaje Navaja suiza apoyado por un framework que será la solución óptima para todos los problemas del mundo. La simple realidad es que ese lenguaje o framework no existe. ¿Por qué? Bueno, la solución de problemas informáticos es siempre acerca de adoptar compromisos que optimicen la solución en determinadas condiciones. Si tu problema cumple esas condiciones, perfecto. Si no, estarás usando una herramienta subóptima.

[...]

Es importante tener opciones para resolver los problemas de cada día. Entender las herramientas y su aplicabilidad a un problema dado debería ser nuestro trabajo

En resumen, en lugar de criticar herramientas, aprende su ámbito de aplicación...

La misma entrada en BP

domingo, diciembre 03, 2006

sky2 y sk98lin en Ubuntu

Nota: Esta entrada es obsoleta, porque desde la versión 2.6.17 el driver sky2 funciona correctamente y sk98lin ha dejado de mantenerse. O sea, que hay volver a configurar sky2 y hay que eliminar del fichero /etc/modprobe.d/blacklist la línea blacklist sky2 (si existe)

Referencias:
sk98lin en la wiki de gentoo en la que se explica un poco la historia
Una explicación de la situación el la lista del kernel


Entrada original:

Tenía problemas con el driver de la tarjeta de red, una Marvell Yukon, desde la actualización a Dapper y no lo sabía, porque no la usaba últimamente. Si se hace un uso intensivo de la red (léase P2P ;) ) sky2, que es el driver que ubuntu coge por defecto, se quedaba atascado. El caso es que he buscado (quizás no mucho) y no he encontrado, como suele ser usual, la receta sencilla. Lo que me ha servido a mi es:

  1. Instalar las cabeceras del kernel (en mi caso y salvo casos excepcionales linux-headers-generic)
  2. sudo apt-get install build-essential (si no lo tenemos)
  3. Bajar el driver de http://www.marvell.com/drivers/driverSearchResults.do y descomprimirlo
  4. Sustiuir #!/bin/sh por #!/bin/bash en el fichero install.sh
  5. En el directorio descomprimido: sudo ./install.sh y damos a 1. Installation
  6. sudo modprobe sk98lin
  7. añadir la línea blacklist sky2 al final del fichero /etc/modprobe.d/blacklist (edítalo como root o con sudo)
Espero que le sea útil a alguien...
(Basada en dos recetas, para mi, incompletas: http://www.ubuntu-es.org/index.php?q=node/30706 y http://ubuntuforums.org/showthread.php?t=176096 )

La misma entrada en BP

jueves, noviembre 30, 2006

Programadores e interfaces de usuario

Hay algunos brillantes, pero en general casi todos los programadores que conozco son(mos) muy malos diseñando interfaces de usuario. Esto tiene varios motivos, creo:

  • El programador tiende a ver la funcionalidad, no el uso.

  • El programador no tiene la formación adecuada (ni el interés(?)) acerca de usabilidad ni diseño. Quizás tampoco la formación adecuada en programación de GUI's

  • El programador, casi por definición es vago. Gastará el mínimo tiempo en el diseño del interfaz


También hay algunos aspectos que dificultan que un interfaz sea armonioso y usable: los continuos cambios de especificaciones, la indefinición de funcionalidad y de diseño:
- Eso... uhmmm... déjalo también configurable
- Ya, ¿pero dónde?
- Ya le encontrarás un hueco...
- Con tantas opciones ¿no será un poco difícil de usar?
- ...
De todos modos lo que a mi me parece adecuado es que el programador programe un interfaz de usuario diseñado por un diseñador conjuntamente con el cliente, o al menos validado por éste. Si no hay más remedio que diseñarlo por lo menos intentar no hacer el diálogo. En ese mismo enlace hay consejos sencillos sobre lo que no hay que hacer. El ejemplo del GUI de wget es espeluznante... :)

La misma entrada en BP

miércoles, noviembre 22, 2006

Communicating Sequential Processes (CSP)

Hace unos días leyendo una noticia de Slashdot me llamó la atención un comentario en el que se hacía referencia a "Communicating Sequential Processes" (CSP), que según la wikipedia inglesa es un lenguaje de descripción de patrones de interacción en sistemas concurrentes (que dicho así todo seguido suena casi peor...) Según la propaganda ayuda tanto a especificar correctamente el modelo de concurrencia como a verificarlo y depurarlo. Tiene una página oficial, Using CSP en la que se puede descargar el libro Communicating Sequential Processes de 1985 en pdf. Además existen implementaciones para usarlo tanto en Java, JCSP, como en C++, C++CSP. No he tenido tiempo de estudiarlo con tranquilidad, pero lo dejo por aquí por si resulta interesante. Si alguien lo ha estudiado o usado puede ofrecer su opinión al respecto...

La misma entrada en BP

martes, noviembre 21, 2006

Un vistazo a C++09

Acabo de ver referenciado en OSnews:What's Coming in C++ '09 un artículo que resume las últimas decisiones acerca del nuevo estándar de C++: C++09: A Glimpse into the Future. Como decía Ion Gaztañaga parece que la x de C++0x se despeja: intentarán que sea en 2009. Por lo demás, lo que se viene hablando por aquí, rvalue-refrence, conceptos, deducción de tipos en las inicializaciones (¡que bien!, usar auto, ver "Un vistazo a C++0x" por Bjarne Stroustrup), delegación de constructores...

La misma entrada en BP

lunes, noviembre 13, 2006

Enlaces varios (IV)

En la linea de cantidad sobre calidad, ración de enlaces apresurados:
La misma entrada en BP

miércoles, noviembre 08, 2006

Análisis estático de código: flawfinder

Gracias a una entrada en el valle del viento helado de Drizzt, "Algunos enlaces de seguridad", he conocido la existencia de flawfinder, un analizador estático de código (C/C++). Te da alertas de código potencialmente inseguro, sobre todo de la utilización de funciones y prácticas con largo historial de vulnerabilidades. No añade nada que no debiéramos saber ya, pero siempre es diferente saber que tal o cual práctica es potencialmente peligrosa y otra es verlo en tu código ;)

Por dar alguna información más acerca de este tipo de programas en la propia página de éste vienen enlaces a otros similares: splint, cqual.... si hubiese seguido la bitácora de fernand0 como se merece, me habría enterado hace un año: " Análisis estático del código fuente y seguridad"


La misma entrada en BP

lunes, noviembre 06, 2006

Concurrencia y librerías reusables

Vía Thinking Parallel: News for Weeks 43 and 44 / 2006 me encuentro con un extenso artículo acerca del impacto de la concurrencia en las librerías: Concurrency and the impact on reusable libraries.

Es algo sabido que el diseño de las librerías debería tener en cuenta si su uso va a ser concurrente. Y debido a que los tiempos están cambiando es muy probable que lo sea. Si nos damos cuenta a posteriori necesitaremos usar muletas :)


La misma entrada en BP