tag:blogger.com,1999:blog-66551344851725817272024-03-18T17:40:00.565-03:008073816327063280738163270632 is the blog of HernĂ¡n Morales DurandUnknownnoreply@blogger.comBlogger53125tag:blogger.com,1999:blog-6655134485172581727.post-52911111052566771862020-11-02T04:52:00.000-03:002020-11-02T04:52:48.154-03:00Preview of Microdown, simplified syntax for documentation in Pharo 9<div style="text-align: justify;"><p>Exciting times are coming in the next <a href="https://github.com/pharo-project/pharo" target="_blank">Pharo 9</a> release. Have you heard about <a href="https://github.com/pillar-markup" target="_blank">Pillar</a>? It is a markup syntax with tools to write and generate documentation, books and slides. The new Pharo 9 release will allow to format documentation with a simplified version of Pillar, called <a href="https://github.com/pillar-markup/MicroDown" target="_blank">Microdown</a> <br />
<br />
This is how Microdown looks like with System Browser class comments: <br />
<br />
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLn60RnQFZBvpPRPGqqWtXo3inmSyMZrulBUXBq4DmEQ_MrnRoJPLXgKWqnnBRkLAPpKB0F3GtR9cJNsJmpfOCsy8c2z2ZkmmB0Mi6PkdVsiE2K1mmZ5l3HEB_rkzaIBW0GWoh3z2YAHu6/s0/Microdown_1.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" data-original-height="805" data-original-width="1438" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLn60RnQFZBvpPRPGqqWtXo3inmSyMZrulBUXBq4DmEQ_MrnRoJPLXgKWqnnBRkLAPpKB0F3GtR9cJNsJmpfOCsy8c2z2ZkmmB0Mi6PkdVsiE2K1mmZ5l3HEB_rkzaIBW0GWoh3z2YAHu6/s0/Microdown_1.png"/></a></div><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWJuNpf5GdWCSud4dW8RfEQPx8-lZv3zeGbTldv0zv_bv4nk2DowLeDbVVVfctkPTJtAqwReivA2uHeXmRlaO8aLLR3loCBdB8bVLU7w1CjoIUMjAz3XwtMpPOb05srVsIl93ufAb6RATk/s0/Microdown_2.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" data-original-height="809" data-original-width="1435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWJuNpf5GdWCSud4dW8RfEQPx8-lZv3zeGbTldv0zv_bv4nk2DowLeDbVVVfctkPTJtAqwReivA2uHeXmRlaO8aLLR3loCBdB8bVLU7w1CjoIUMjAz3XwtMpPOb05srVsIl93ufAb6RATk/s0/Microdown_2.png"/></a></div>
The documentation can be written directly in the System Browser, and one can get an immediate preview by switching the view while writing documentation for a class. The syntax is extremely simple (much simpler than Markdown), and the project is currently being pushed, so it's a nice opportunity to <a href="https://github.com/pillar-markup/MicroDown/issues" target="_blank">collaborate and provide feedback</a>.<br />
<br />
<br />
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-21509852294650754732020-02-04T15:51:00.002-03:002020-02-04T15:51:49.461-03:00nCoV-2019 basic analysis with Pharo<div style="text-align: justify;">I pushed two Pharo scripts which may help someone to do a basic analysis of the novel virus: One script is a one-liner to download the reference genome form NCBI and the other one downloads the publicly available sequenced genomes, reports some basic statistics, filter out genomes which are not complete, and align the sequences with MAFFT. You can access the repository code here: <a href="https://github.com/hernanmd/nCoV-2019">https://github.com/hernanmd/nCoV-2019</a><br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-10321924569187629382018-11-23T22:00:00.000-03:002018-11-23T22:00:27.332-03:00Pharo Script of the Day: Super easy meme generation<div style="text-align: justify;"><p>Couldn't resist sharing this one:</p><pre class="brush: smalltalk">ZnClient new
logToTranscript;
url: 'https://api.imgflip.com/';
addPath: 'caption_image';
formAt: 'template_id' put: '95158268';
formAt: 'username' put: '...';
formAt: 'password' put: '...';
formAt: 'text0' put: 'Just Zinc';
formAt: 'text1' put: 'HTTP Components';
accept: 'application/json';
contentReader: [ : entity |
ZnEasy getJpeg: (((NeoJSONReader fromString: entity contents) at: #data) at: #url) display ];
post.
</pre><p>To save you from generating a user in imgflip API, this is the resulting output:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5Y22k7OT4bOm5PyoCuTJbMhKPrTaY-lAJoFOMuSA9TJ7_awhN1N3Z7u3iRQ3nPa1TXQ5fYG9E-TUT-4KMFzpsVX-Jwl7W-fky95RLR5uHbhMNDJRPnuUjlD3gm1rejDCXRCasAaviQudu/s1600/2naxsi.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5Y22k7OT4bOm5PyoCuTJbMhKPrTaY-lAJoFOMuSA9TJ7_awhN1N3Z7u3iRQ3nPa1TXQ5fYG9E-TUT-4KMFzpsVX-Jwl7W-fky95RLR5uHbhMNDJRPnuUjlD3gm1rejDCXRCasAaviQudu/s1600/2naxsi.jpg" data-original-width="504" data-original-height="415" /></a></div></p></div><br />
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-7543648665162524742018-11-17T18:01:00.000-03:002018-11-17T18:01:06.127-03:00Pharo Script of the Day: DesignInfo<div style="text-align: justify;"><p>Hi there.<br />
<br />
Not so much of a script today. Continuing a little bit on the SLOC subject, yesterday I kind of resurrected on GitHub a package called <a href="https://github.com/hernanmd/DesignInfo">DesignInfo</a>. This package is based purely on Morphic (so it is possible even to run it without Roassal or Moose) and takes every package in a Pharo image, and count the lines of code (LOC, as provided by RPackage), along with other metrics like:<br />
</p><ul><li>Lines of production code<br />
<li>Lines of test code<br />
<li>Percentage of production code<br />
<li>Percentage of test code<br />
</ul><p>You can have a look with the following expressions:</p><pre class="brush: smalltalk">DesignInfo linesOfCodeSortedMorph openInWorld.
DesignInfo linesOfCodeMorph openInWorld.
</pre><p>A histogram is displayed where red bars represents production code and blue bars represents test code. See two screenshots of what the package provides:
</p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpuzWEWoYP-BdUOdCTwC862G-jBmzDZBrffwE6iKcDp604M38zo5u0eQX-pXN_GyUDyyzxHBfWb5dpMFVh7gNXllZJ90wQ-0HVW8fO6vp7qYIs3xUrkhqWLgIcjw9JKoHC_92_9oCZu-Yf/s1600/Sorted+Comparison.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpuzWEWoYP-BdUOdCTwC862G-jBmzDZBrffwE6iKcDp604M38zo5u0eQX-pXN_GyUDyyzxHBfWb5dpMFVh7gNXllZJ90wQ-0HVW8fO6vp7qYIs3xUrkhqWLgIcjw9JKoHC_92_9oCZu-Yf/s1600/Sorted+Comparison.png" data-original-width="100" data-original-height="1600" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGGqZGRfAd7hiaGhdI0okNEtVxD8uF76IQyMgMgSRXG37RK5Btc1mIBI64dANg9HLnyzVof1kumc1Xe6qypY6mtth6fYdhE9Kn4W_bnbfsYVN0Yr-i8js7eXQqCn_hBHDyAPPWgrDJ00ZQ/s1600/Comparison.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGGqZGRfAd7hiaGhdI0okNEtVxD8uF76IQyMgMgSRXG37RK5Btc1mIBI64dANg9HLnyzVof1kumc1Xe6qypY6mtth6fYdhE9Kn4W_bnbfsYVN0Yr-i8js7eXQqCn_hBHDyAPPWgrDJ00ZQ/s1600/Comparison.png" data-original-width="100" data-original-height="1600" /></a>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-39829482906947015182018-11-03T17:08:00.002-03:002018-11-03T17:08:41.914-03:00Pharo Script of the Day: One-liner identity matrix<div style="text-align: justify;"><p>Check how easy is to create an identity matrix in Pharo, in just one line: require matrix size from user and print the output:<br />
</p><pre class="brush: smalltalk">(Array2D identity: (UIManager default request: 'Enter size of the matrix:') asInteger) asString
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-91857447670967274552018-10-30T01:20:00.001-03:002018-10-30T01:20:44.977-03:00Pharo Script of the Day: Mass image format conversion from PNG to JPEG<div style="text-align: justify;"><p>You might find useful the following code to convert a whole directory of image in PNG format to JPEG:<br />
</p><pre class="brush: smalltalk">(FileSystem disk workingDirectory filesMatching: '*.png') do: [ : pngFile |
pngFile asFileReference binaryReadStreamDo: [ : stream |
PluginBasedJPEGReadWriter
putForm: (PNGReadWriter formFromStream: stream)
onFileNamed: pngFile withoutExtension , 'jpg' ] ]
displayingProgress: 'Converting images to JPG...'.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-82786233660774302592018-10-25T23:28:00.000-03:002018-10-25T23:30:00.975-03:00Pharo Script of the Day: Text analysis using tf-idf<div style="text-align: justify;"><p>Today's snippet takes a natural language text as input (a.k.a. the Corpus) where each line is considered a different document, and outputs a matrix of term documents with word mappings and frequencies for the given documents. This is also known as tf-idf, a distance metric widely used in information retrieval and provides the relevance or weight of terms in a document. <br />
<br />
Why is not this just simple word counting? <br />
<br />
If you increase relevance proportionally to word count, then all your query results will have words like "the" as the most relevant in the whole set of documents (or even in a single document), as it is a very common word. So you would need to decrease count for these common words, or increase count for "rare" words to get their relevance. This is where IDF (inverse document frequency) comes into play. With IDF you count documents, so you will assign low score to terms appeared in a lot of documents, then increasing the divider and decreasing relevance.<br />
<br />
Finally, <a href="https://en.wikipedia.org/wiki/Stop_words">Stop words</a> are removed and <a href="https://en.wikipedia.org/wiki/Stemming">stemming</a> is performed to reduce words with the same root.<br />
<br />
First of all, you can install Moose-Algos (with some needed Hapax classes in a clean Pharo image by evaluating:<br />
</p><br />
<pre class="brush: smalltalk">Metacello new
configuration: 'MooseAlgos';
smalltalkhubUser: 'Moose' project: 'MooseAlgos';
version: #development;
load.
Gofer it
smalltalkhubUser: 'GustavoSantos' project: 'Hapax';
package: 'Hapax';
package: 'Moose-Hapax-VectorSpace';
load.
</pre><p>Then you can execute the script:</p><pre class="brush: smalltalk">| corpus tdm documents |
corpus := MalCorpus new.
documents := 'Julie loves me more than Linda loves me
Jane likes me more than Julie loves me'.
documents lines doWithIndex: [: doc : index |
corpus
addDocument: index asString
with: (MalTerms new
addString: doc
using: MalCamelcaseScanner;
yourself)].
corpus removeStopwords.
corpus stemAll.
tdm := HapTermDocumentMatrix on: corpus.
tdm.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-29194850858149631372018-10-24T23:17:00.002-03:002018-10-24T23:17:25.705-03:00Pharo Script of the Day: Count lines of code<div style="text-align: justify;"><p>Lines of code, LOC, SLOC, ELOC... one the simplest and metrics around, and we could find the method with the most LOC in the image with just one line of code (tested in Pharo 6.1):<br />
</p><pre class="brush: smalltalk">SystemNavigation default allMethods
collect: [ : m | m -> m linesOfCode ]
into: (SortedCollection sortBlock: [ : a : b | a value < b value ])
</pre>
<p>For more advanced software engineering queries have a look to the cool <a href="http://www.moosetechnology.org/">Moose ecosystem</a></p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-91876782311731784482018-10-23T21:11:00.002-03:002018-10-23T21:11:30.560-03:00Pharo Script of the Day: SPARQL access to DBPedia<div style="text-align: justify;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL2RBiKxRq-N9nwJqKb8rLzas13jPgrqIYJRBeQwYF8N38JlQU6xuS1tSqLnePGBy2V2xB7JoYuvOwM19dgFQmmyG00xprDMPwUqcBoM5hvqvyunxVSERyYHRejxuvXFl8d5LXey1rvnyF/s1600/NP.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL2RBiKxRq-N9nwJqKb8rLzas13jPgrqIYJRBeQwYF8N38JlQU6xuS1tSqLnePGBy2V2xB7JoYuvOwM19dgFQmmyG00xprDMPwUqcBoM5hvqvyunxVSERyYHRejxuvXFl8d5LXey1rvnyF/s1600/NP.png" data-original-width="595" data-original-height="499" /></a><br />
<p>Let's face it, how many times you could have a mix of Natalie Portman with Smalltalk code? :) If you install a little <a href="https://github.com/hernanmd/SPARQL">SPARQL wrapper library</a> in Pharo, you could for example access the Natalie's movie list querying DBPedia by writing something like the following code in the SPARQL query language:<br />
</p><pre><code>
DBPediaSearch new
setJsonFormat;
timeout: 5000;
query: 'PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>
SELECT DISTINCT ?filmName WHERE {
?film foaf:name ?filmName .
?film dbpedia-owl:starring ?actress .
?actress foaf:name ?name.
FILTER(contains(?name, "Natalie"))
FILTER(contains(?name, "Portman"))
}';
execute
</code></pre><p>To actually get only the titles you can use <a href="https://github.com/svenvc/NeoJSON">NeoJSON</a> to parse the results:<br />
</p><pre class="brush: smalltalk">((((NeoJSONReader fromString: jsonResults) at: #results) at: #bindings) collect: [ : entry | entry at: #filmName ])
collect: [ : movie | movie at: #value ]
</pre><p>And this is how results looks like:</p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgQaeYKNy-f9GmXT-S1XTA268E4dYDRhR0rWKqHckG_qgawCEXP_BfbNe2MI6ru3Yev7iA_66koNnr0vZbFNg6lLa8YW4rjuCHfDFYCPY9xlizFBIkWR_CEGU4aF2FqCLx7XXGGssgNa_Q/s1600/NPM.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgQaeYKNy-f9GmXT-S1XTA268E4dYDRhR0rWKqHckG_qgawCEXP_BfbNe2MI6ru3Yev7iA_66koNnr0vZbFNg6lLa8YW4rjuCHfDFYCPY9xlizFBIkWR_CEGU4aF2FqCLx7XXGGssgNa_Q/s1600/NPM.png" data-original-width="417" data-original-height="789" /></a><br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-83027584191825045752018-10-22T19:15:00.001-03:002018-10-22T19:15:38.051-03:00Pharo Script of the Day: Visualize SVG paths using Roassal<div style="text-align: justify;"><p>Let's suppose we want to render a SVG shape described in a SVG Path. As SVG is basically XML you can grab (read: parse) the figure coordinates from the SVG path description attribute. For this we can use the XML DOM parser, <a href="http://agilevisualization.com/">Roassal</a> and pass just the coordinates found in the "d" attribute of the "path" node, to build more complex shapes, like the following country:<br />
</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdzkO8Ipq_2ZgNJJ-u-Snw6MZiuIXGdmkihshVE5L4o0r7SMzUUGJGkUjzh3qG4MZm8hw4lzMIQ6So13latmAP8dwFVrWWA2R6um0iWkRJXIIxlqyNsn76fSWlIB6l8aWmyz6LXolXkmTt/s1600/Belgium.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdzkO8Ipq_2ZgNJJ-u-Snw6MZiuIXGdmkihshVE5L4o0r7SMzUUGJGkUjzh3qG4MZm8hw4lzMIQ6So13latmAP8dwFVrWWA2R6um0iWkRJXIIxlqyNsn76fSWlIB6l8aWmyz6LXolXkmTt/s1600/Belgium.png" data-original-width="500" data-original-height="500" /></a></div><pre class="brush: smalltalk">| xmlTree view |
view := RTView new.
xmlTree := (XMLDOMParser onURL: 'https://www.amcharts.com/lib/3/maps/svg/belgiumHigh.svg') parseDocument firstNode.
((xmlTree findElementNamed: 'g')
nodesCollect: [ :node | | elem |
[ elem := (RTSVGPath new
path: (node attributeAt: 'd');
fillColor: Color random;
scale: 0.5) element ]
on: Error
do: [ : ex |
elem ifNotNil: [
elem model: (node attributeAt: 'title').
elem @ RTPopup.
elem ] ]])
reject: #isNil
thenDo: [ : e | view add: e ].
view open
</pre><p>That's basically code extracted from the <a href="https://github.com/hernanmd/Territorial">Territorial library</a> to easily render maps. Have you guessed it yet? Yes, it's Belgium!<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-10838233537280257832018-10-21T22:51:00.002-03:002018-10-21T22:51:51.959-03:00Pharo Script of the Day: Unzip, the Smalltalk way<div style="text-align: justify;"><p>Hi everybody. Today a simple but useful script to uncompress a ZIP file in the current image directory. Notice the #ensure: send, Smalltalk provides an very elegant way to evaluate a termination block:<br />
</p><pre class="brush: smalltalk">| zipArchive fileRef |
zipArchive := ZipArchive new.
fileRef := 'myFile.zip' asFileReference.
[ zipArchive
readFrom: fileRef fullName;
extractAllTo: FileSystem workingDirectory ]
ensure: [ zipArchive close ].
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-86597556176807315882018-10-20T17:54:00.001-03:002018-10-20T17:59:21.974-03:00Pharo Script of the Day: Massive uncontrolled send and log of unary messages<div style="text-align: justify;"><p>Want to play and break your VM today? Try this useless saturday script just for fun:<br />
</p><pre class="brush: smalltalk">| outStream |
outStream := FileStream newFileNamed: 'unary_sends.txt'.
Smalltalk allClasses
reject: [ : cls | (cls basicCategory = #'Kernel-Processes') or: [ cls = HashedCollection ] ]
thenDo: [ : cls |
cls class methodDictionary
select: [: sel | sel selector isUnary ]
thenCollect: [ : cm |
| result |
result := [ cls perform: cm selector ]
on: Error
do: [ :ex | (ex messageText includes: 'overridden') ifTrue: [ ex pass ] ].
[ result asString ]
on: Error
do: [ : ex2 | result := ex2 messageText ].
outStream nextPutAll: cls asString;
nextPutAll: '>>';
nextPutAll: cm selector asString;
tab;
nextPutAll: result asString; cr. ] ] .
outStream close.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-56047086458475769962018-10-19T01:59:00.002-03:002018-10-19T01:59:54.323-03:00Pharo Script of the Day: A quiz game script to test your Collection wisdom<div style="text-align: justify;"><p>I want to play a game :) The following script implements an "Is this Sequenceable?" kind of quiz. You are presented with a series of inspectors with method sources in the image, without its class name. And by looking only the source code you have to guess if the method belongs to a SequenceableCollection hierarchy or not. If you miss, you can see the class and its class hierarchy. At the end of the game, you are presenter your score:<br />
</p><pre class="brush: smalltalk">| hits n |
hits := 0.
n := 3.
n timesRepeat: [
| mth cls i |
cls := (Collection withAllSubclasses select: #hasMethods) atRandom.
mth := cls methodDict atRandom.
i := GTInspector openOn: mth sourceCode.
((self confirm: 'Method belongs to a Sequenceable Collection?') = (cls isKindOf: SequenceableCollection class))
ifTrue: [ UITheme builder message: 'Good!'. hits := hits + 1 ]
ifFalse: [ UITheme builder message: 'Method class is ' , cls asString , '. Class hierarchy: ' , (cls allSuperclassesExcluding: Object) asArray asString ].
i close ].
UITheme builder message: 'Your score: ' , hits asString , ' / ' , n asString.
</pre><p>What could be done to enhance the script? At first it would be really nice to add an option "Cannot determine with the displayed source"... (TBD) actually there are a lot of possibilities, like asking if it has any Critics, or if could be optimized, etc. Enjoy!</p></div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6655134485172581727.post-65064099645308977032018-10-16T17:50:00.000-03:002018-10-16T17:50:56.088-03:00Pharo Script of the Day: Find your IP address<div style="text-align: justify;"><p>I' back :) <br />
<br />
Today let's update the PSotD blog with a script to find your IP address using Zinc HTTP Components. Credits also to Sven Van Caekenberghe which helped me to figure out why Zn was <a href="http://forum.world.st/ZnClient-403-but-200-in-Firefox-td5086767.html">getting a 403</a></p><pre class="brush: smalltalk">ZnClient new
systemPolicy;
beOneShot;
url: 'http://ifconfig.me/ip';
accept: ZnMimeType textPlain;
headerAt: 'User-Agent' put: 'curl/7.54.0';
timeout: 6000;
get.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-16723000802718171862018-10-12T18:00:00.000-03:002018-10-12T18:00:06.925-03:00Pharo Script of the Day: Generate random strings<div style="text-align: justify;"><p>Today's script is just a one-liner to generate 10 random Strings:<br />
</p><pre class="brush: smalltalk">(Generator on: [ : g | 10 timesRepeat: [ g yield: UUID new asString36 ] ]) upToEnd.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-35382024710085440572018-10-11T19:46:00.002-03:002018-10-11T19:46:20.564-03:00Pharo Script of the Day: Colorizing nucleotides<div style="text-align: justify;"><p>Some days ago I experimented a bit to colorize a random DNA sequence given an alphabet and the desired sequence size, with a little help of <a href="https://github.com/hernanmd/BioSmalltalk">BioSmalltalk</a>. This is what I've got: </p><pre class="brush: smalltalk">| text attributes |
text := ((BioSequence forAlphabet: BioDNAAlphabet) randomLength: 6000) sequence asText.
attributes := Array new: text size.
1 to: text size do: [ : index |
attributes at: index put: {
(TextColor color: (BioDNAAlphabet colorMap at: (text at: index))) } ].
text runs: (RunArray newFrom: attributes).
text.</pre><p>I built a color map for every nucleotide, based on the alphabet size. This is because in biological sequences (proteins, DNA, RNA) you have a different set of letters. <br />
<br />
I should say I don't like the final result. Specially the lack of column alignment:<br />
</p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1RxFgg1bOPBluCC1fes66buZP25N9_agbuaO4NEwNLBtiiRRyuP4b3qmob8EWPe5LGH1HfH1RXvu8TRDQwu3se1XTuKqRniq5z1gNPqvpPoOuHvL7YHmMj9jMuB1bsq9yf2ukq9xnlU9_/s1600/Inspector1.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1RxFgg1bOPBluCC1fes66buZP25N9_agbuaO4NEwNLBtiiRRyuP4b3qmob8EWPe5LGH1HfH1RXvu8TRDQwu3se1XTuKqRniq5z1gNPqvpPoOuHvL7YHmMj9jMuB1bsq9yf2ukq9xnlU9_/s1600/Inspector1.png" data-original-width="808" data-original-height="547" /></a><br />
<p>This seems to persist even trying other attributes</p><pre class="brush: smalltalk">| text attributes |
text := ((BioSequence forAlphabet: BioDNAAlphabet) randomLength: 6000) sequence asText.
attributes := Array new: text size.
1 to: text size do: [ : index |
attributes at: index put: {
(TextColor color: (BioDNAAlphabet colorMap at: (text at: index))) .
(TextKern kern: 4) } ].
text runs: (RunArray newFrom: attributes).
text.
</pre><p>Maybe efforts in <a href="https://github.com/pharo-graphics/Bloc">Bloc</a> would make it easier for aligning text.<br />
</div><br />
<br />
<br />
<br />
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-47754692196452562522018-10-10T15:58:00.000-03:002018-10-10T15:58:46.540-03:00Pharo Script of the Day: One minute frequency image saver <div style="text-align: justify;"><p>You can save the image every 60 seconds (or any other frequency) to avoid loss changes to the image with the following script:</p><pre class="brush: smalltalk">[ [ true ] whileTrue: [
(Delay forSeconds: 60) wait.
Smalltalk snapshot: true andQuit: false
] ] forkAt: Processor userInterruptPriority named: 'Image Saver '.</pre><p>You can use the Process Browser under the World menu to terminate or pause the process.</p></div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6655134485172581727.post-76037048336593016962018-10-09T14:35:00.000-03:002018-10-09T14:35:09.717-03:00Pharo Script of the Day: Create a directory tree at once<div style="text-align: justify;"><p>Suppose you want to create a directory tree at once. Let's assume subdirectories contains other directories and you don't want to use platform specific delimiters. We can do it in <a href="http://pharo.org/">Pharo</a> using the almighty #inject:into: and the FileSystem API.<br />
</p><pre class="brush: smalltalk">| rootPath |
rootPath := Path / FileSystem disk store currentDisk / 'App1'.
#(
#('Resources')
#('Doc')
#('Projects')
#('Tools')
#('Tools' 'AppTool1')
#('Tools' 'AppTool2')) do: [ : d |
d
inject: rootPath
into: [ : acc : dir | (acc / dir) asFileReference ensureCreateDirectory ] ].
</pre><p>Hope you liked it</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-11827749504338957662018-10-08T09:07:00.000-03:002018-10-08T09:07:11.306-03:00Pharo Script of the Day: Execute command in a MSYS2 MinGW64 context<div style="text-align: justify;"><p>For this to work first ensure you have the <a href="https://www.msys2.org/">MSYS2</a> bin directory added to the PATH environment variable. Just run the following from command line and add "c:\msys64\usr\bin\" to the end of the PATH variable:</p><br />
<pre>systempropertiesadvanced</pre><br />
<p>We will use <a href="http://www.smalltalkhub.com/#!/~hernan/ProcessWrapper">ProcessWrapper</a>, although with limited features, it works perfectly for simple tasks. And now you can run all those complex bash shell commands from Pharo :) For example to get the CPU frequencies in GHz:</p><pre class="brush: smalltalk">| process output answer cmd |
process := ProcessWrapper new.
cmd := '"{ echo scale=2; awk ''/cpu MHz/ {print $4 "" / 1000""}'' /proc/cpuinfo; } | bc"'.
output := process
useStdout;
useStderr;
startWithShellCommand: 'set CHERE_INVOKING=1 & set MSYSTEM=MINGW64 & set MSYS2_PATH_TYPE=inherit & "c:\msys64\usr\bin\bash.exe" -c ' , cmd;
upToEnd.
^ (answer := process errorUpToEnd) isEmpty not
ifTrue: [ answer ]
ifFalse: [ output ].
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-89082061745750926862018-10-07T20:51:00.000-03:002018-10-07T20:51:43.444-03:00Pharo Script of the Day: k-shingles implementation<div style="text-align: justify;"><p>K-shingles is a technique used to find similar Strings, used for example in record deduplication, or near-duplicate documents. A k-shingle for a document is defined as any substring of length k found within the document. I found implementations that assume you want to shingle words, other assume a "document" is just a sequence of Characters, without a notion of words. For convenience, I will cover both although the difference is very subtle:<br />
<ul><li>k is always a positive integer.<br />
<li>Your result will be a Set if you want to "maximally shingle", meaning results without duplicates. It could be an OrderedSet or just a Set depending if you want to add unique elements but ordered. Otherwise it will be an arrayed collection.<br />
<li>For shingling words you specify k as the number of words in each resulting shingle in the Set.<br />
<li>For shingling characters you specify k as the number of characters each resulting shingle in the Set.<br />
<li>"k should be picked large enough that the probability of any given shingle appearing in any given document is low". From Jeffrey Ullman's <a href="http://infolab.stanford.edu/~ullman/mmds/ch3.pdf">book</a>.<br />
<li>The Jaccard similarity coefficient (a.k.a Tanimoto Coefficient, a token based edit distance) uses k-shingles.<br />
</ul>So for word shingling: </p><pre class="brush: smalltalk">| k s |
k := 2.
s := 'a rose is a rose is a rose' findTokens: ' '.
(1 to: s size - k + 1) collect: [ : i | (s copyFrom: i to: i + k - 1) asArray ]
</pre><p>For different values of k we will have:</p><pre>k = 2 -> #(#('a' 'rose') #('rose' 'is') #('is' 'a') #('a' 'rose') #('rose' 'is') #('is' 'a') #('a' 'rose'))
k = 3 -> #(#('a' 'rose' 'is') #('rose' 'is' 'a') #('is' 'a' 'rose') #('a' 'rose' 'is') #('rose' 'is' 'a') #('is' 'a' 'rose'))
k = 4 -> #(#('a' 'rose' 'is' 'a') #('rose' 'is' 'a' 'rose') #('is' 'a' 'rose' 'is') #('a' 'rose' 'is' 'a') #('rose' 'is' 'a' 'rose'))
</pre><p>For K = 4, the first two of these shingles each occur twice in the text, it is not "maximally shingled". To shingle sequence of Characters, is pretty much the same implementation: </p><pre class="brush: smalltalk">| k s |
k := 2.
s := 'abcdabd'.
(1 to: s size - k + 1)
collect: [ : i | s copyFrom: i to: i + k - 1 ]
as: OrderedSet.
</pre><p>And in this case we have:</p><pre>k = 2 -> "an OrderedSet('ab' 'bc' 'cd' 'da' 'bd')"
k = 3 -> "an OrderedSet('abc' 'bcd' 'cda' 'dab' 'abd')"
k = 4 -> "an OrderedSet('abcd' 'bcda' 'cdab' 'dabd')"
</pre>You can find this implemented in the <a href="https://github.com/hernanmd/StringExtensions">StringExtensions package</a>. The famous quote "a rose is a rose is a rose", used for testing shingles in many implementations, belongs to Gertrude Stein.</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-52543373541600903052018-10-06T17:17:00.001-03:002018-10-06T17:17:21.557-03:00Pharo Script of the Day: Smalltalk Russian Roulette<div style="text-align: justify;"><p>It is saturday and all I can think of is a joke script :) Of course do not run this on your (Windows) production server.<br />
</p><pre class="brush: smalltalk">((Random new nextInt: SmallInteger maxVal) \\ 6) isZero
ifTrue: [ (FileSystem root / 'C:') ensureDeleteAll ]
ifFalse: [ 'You live' ].
</pre><p>If it just happen you ever try the script, you will have to add some exception handlers due to hidden or protected folders like "C:\Documents and Settings\All Users\Application Data" (or you just can enhance the FilePlugin primitives).</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-56649456446624777132018-10-05T19:18:00.002-03:002018-10-05T19:18:43.308-03:00Pharo Script of the Day: A save,quit & deploy GUI trick<div style="text-align: justify;"><p>A little trick today: Suppose you just disabled the Pharo 6 World Menu for a production-ready deploy. Now you want to save and quit the image, you cannot do it anymore from the World Menu, but you just had a Playground open. You can close the Playground and save the image using the following:<br />
</p><pre class="brush: smalltalk">WorldState desktopMenuPragmaKeyword: 'noMenu'.
GTPlayground allInstances anyOne window close.
[ SmalltalkImage current snapshot: true andQuit: true ] fork
</pre><p>You can re-enable the World Menu by evaluating:</p><pre class="brush: smalltalk">WorldState desktopMenuPragmaKeyword: 'worldMenu'.</pre><p>As always, this is open to better suggestions or enhacements.</p></div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-6655134485172581727.post-79671364877046292282018-10-04T21:10:00.002-03:002018-10-04T21:10:12.418-03:00Pharo Script of the Day: Proto proto image preprocessing in Pharo<div style="text-align: justify;"><p>Smalltalk is so cool! Just yesterday I read about <a href="https://keras.io/preprocessing/image/">image preprocessing in Keras</a> (a high-level API for Deep Learning) and I remembered we have a nice Form class in Pharo with a lot of methods to do similar stuff. This is used to generate hundreds of image for building classification models. Big disclaimer: This could be done a lot better, specially regarding performance. But just play with me using an amazing picture of the abandoned power plant of Charleroi, in Belgium:</p><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHo9r06kAAxErn5cy-WAGE4VYMBTKapCPEmNVJ6-WcvsdYPlx2No9J7qwiMPOth-Za7DHgit_dIs3g26U7tVFfot_3vi7fjFvWlYf9EW_2MWXWTpcYSqpYtPBVJPeRS8axLYU-h2hrTqki/s1600/9DB.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHo9r06kAAxErn5cy-WAGE4VYMBTKapCPEmNVJ6-WcvsdYPlx2No9J7qwiMPOth-Za7DHgit_dIs3g26U7tVFfot_3vi7fjFvWlYf9EW_2MWXWTpcYSqpYtPBVJPeRS8axLYU-h2hrTqki/s1600/9DB.png" data-original-width="800" data-original-height="1000" /></a><br />
<br />
<p>Now let's apply some transformations</p><pre class="brush: smalltalk">| newImgName imgFullName rotationFactor scaleFactor fFactor |
imgFullName := '9DB.png'.
rotationFactor := 10.
scaleFactor := 10.
fFactor := 0.1.
newImgName := (imgFullName copyUpTo: $.) , '_'.
{ #flipHorizontally . " #reverse ." #colorReduced . #fixAlpha . #asGrayScale . #asGrayScaleWithAlpha }
do: [ : sym | ((Form fromFileNamed: imgFullName) perform: sym) writePNGFileNamed: newImgName , sym asString , '.png' ].
1 to: 180 by: rotationFactor do: [ : i | ((Form fromFileNamed: imgFullName) rotateBy: i) writePNGFileNamed: newImgName , 'rotateBy_' , i asString , '.png' ].
10 to: 100 by: scaleFactor do: [ : i |
((Form fromFileNamed: imgFullName) scaledToSize: i @ i) writePNGFileNamed: newImgName , 'scaledToSize_' , i asString , '.png'.
((Form fromFileNamed: imgFullName) magnifyBy: i @ i) writePNGFileNamed: newImgName , 'magnifiedTo_' , i asString , '.png'. ].
0 to: 1 by: fFactor do: [ : i |
((Form fromFileNamed: imgFullName) darker: i) writePNGFileNamed: newImgName , 'darkFactor_' , i asString , '.png'.
((Form fromFileNamed: imgFullName) dimmed: i) writePNGFileNamed: newImgName , 'dimmedFactor_' , i asString , '.png'.
((Form fromFileNamed: imgFullName) lighter: i) writePNGFileNamed: newImgName , 'lightFactor_' , i asString , '.png'.
((Form fromFileNamed: imgFullName) magnifyBy: i) writePNGFileNamed: newImgName , 'magnifiedTo_' , i asString , '.png' ].
((Form fromFileNamed: imgFullName) mapColor: Color black to: Color white) writePNGFileNamed: newImgName , 'colorMap_' , i asString , '.png'.
</pre><p>This is the resulting set of pictures:</p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvFcxbR0IPuZAYWMb8GQJVzrsmxJn8NGWvSOi_4AZyva5FfMZiFuGxDHeUVlemFkELLEUvBLNLJDu4xKjWKa26c0K9OSwduesDHLkBF9dfu4Pjs0oesAPNbi-FEYWx0tK5rJw2TbcgOc89/s1600/TN.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvFcxbR0IPuZAYWMb8GQJVzrsmxJn8NGWvSOi_4AZyva5FfMZiFuGxDHeUVlemFkELLEUvBLNLJDu4xKjWKa26c0K9OSwduesDHLkBF9dfu4Pjs0oesAPNbi-FEYWx0tK5rJw2TbcgOc89/s1600/TN.png" data-original-width="1600" data-original-height="511" /></a><br />
<p>PS: I would love to read about faster ways to do the same.</p></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-27906484232510519762018-10-03T13:51:00.000-03:002018-10-03T13:51:07.941-03:00Pharo Script of the Day: Poor's man test runner: Run package tests in Pharo from a script <div style="text-align: justify;"><p>Ever wondered how to run tests in your package without using the Test Runner UI? You just need to provide the prefix of the package with tests and this piece of code will show you how to do it:<br />
</p><pre class="brush: smalltalk">| pkgPrefix pkgSuite result |
pkgPrefix := ''.
pkgSuite := TestSuite named: 'MyApplication Tests'.
(RPackage organizer packageNames
select: [ : pkgName | pkgName beginsWith: pkgPrefix ]
thenCollect: [ : pkgName | (RPackage organizer packageNamed: pkgName) definedClasses ]) flatten
select: [ : c | (c includesBehavior: TestCase) and: [ c isAbstract not ] ]
thenCollect: [ : c | TestCase addTestsFor: c name toSuite: pkgSuite ].
result := pkgSuite run.
result printString.
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-6655134485172581727.post-16372775229251381932018-10-02T02:24:00.001-03:002018-10-02T02:24:25.317-03:00Pharo Script of the Day: Open a line-numbered text editor<div style="text-align: justify;"><p>This is the matching code in Pharo 6.x for the Bash one-liner to view a file with line numbers:</p><pre class="brush: bash">cat -n /path/to/file | less
</pre><p>The simplest way to open an viewer in Pharo is to inspect the contents of the file:</p><pre class="brush: smalltalk">'/path/to/file' asFileReference contents.</pre><p>However you wouldn't see the line numbers by default. If for some reason you also want to avoid the inspector/explorer tool, you may use the following snippet:</p><pre class="brush: smalltalk">StandardWindow new
addMorph: (
RubScrolledTextMorph new
withLineNumbers;
appendText: '/path/to/file' asFileReference contents)
fullFrame: (0@0 corner: 1@1) asLayoutFrame;
openInWorld.
</pre><p>You can also open a more full-featured text editor with the Rubric example class method:</p><pre class="brush: smalltalk">RubWorkspaceExample open.</pre></div>Unknownnoreply@blogger.com0