her.esy.fun/src/Scratch/en/blog/Higher-order-function-in-zsh/index.html

255 lines
22 KiB
HTML
Raw Normal View History

2021-04-18 10:23:24 +00:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>YBlog - Higher order function in zsh</title>
<meta name="keywords" content="zsh, map, foldr, filter, functional, programming, higher order functions" />
<link rel="shortcut icon" type="image/x-icon" href="../../../../Scratch/img/favicon.ico" />
2021-05-25 20:25:47 +00:00
<link rel="stylesheet" type="text/css" href="/css/y.css" />
<link rel="stylesheet" type="text/css" href="/css/legacy.css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
2021-04-18 10:23:24 +00:00
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" href="../../../../Scratch/img/about/FlatAvatar@2x.png" />
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<!-- IndieAuth -->
<link href="https://twitter.com/yogsototh" rel="me">
<link href="https://github.com/yogsototh" rel="me">
<link href="mailto:yann.esposito@gmail.com" rel="me">
<link rel="pgpkey" href="../../../../pubkey.txt">
</head>
<body lang="en" class="article">
<div id="content">
<div id="header">
<div id="choix">
<span id="choixlang">
<a href="../../../../Scratch/fr/blog/Higher-order-function-in-zsh/">French</a>
</span>
<span class="tomenu"><a href="#navigation">↓ Menu ↓</a></span>
<span class="flush"></span>
</div>
</div>
<div id="titre">
<h1>Higher order function in zsh</h1>
</div>
<div class="flush"></div>
<div id="afterheader" class="article">
<div class="corps">
<div>
<img src="../../../../Scratch/img/blog/Higher-order-function-in-zsh/main.jpg" alt="Title image" />
</div>
<div class="intro">
<p>UPDATE: <a href="http://nicholassterling.wordpress.com/2012/03/30/a-zsh-map-function/">Nicholas Sterling had discovered a way to implement anonymous functions</a> Thanks!</p>
<p>With this last version you should use <code>map</code> if you use external function. <code>mapl</code> to use lambda function. And <code>mapa</code> for arithmetic operations.</p>
<p>Example:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode zsh"><code class="sourceCode zsh"><a class="sourceLine" id="cb1-1" title="1">$ filterl <span class="st">'echo $1|grep a &gt;/dev/null'</span> ab <span class="kw">cd</span> ef ada</a>
<a class="sourceLine" id="cb1-2" title="2">ab</a>
<a class="sourceLine" id="cb1-3" title="3">ada</a>
<a class="sourceLine" id="cb1-4" title="4"></a>
<a class="sourceLine" id="cb1-5" title="5">$ folda <span class="st">'$1+$2'</span> <span class="dt">{1..5}</span></a>
<a class="sourceLine" id="cb1-6" title="6">15</a>
<a class="sourceLine" id="cb1-7" title="7"></a>
<a class="sourceLine" id="cb1-8" title="8">$ folda <span class="st">'$1*$2'</span> <span class="dt">{1..20}</span></a>
<a class="sourceLine" id="cb1-9" title="9">2432902008176640000</a>
<a class="sourceLine" id="cb1-10" title="10"></a>
<a class="sourceLine" id="cb1-11" title="11">$ mapl <span class="st">'echo X $1:t Y'</span> ~/.zsh/functional/src/*</a>
<a class="sourceLine" id="cb1-12" title="12">X each Y</a>
<a class="sourceLine" id="cb1-13" title="13">X filter Y</a>
<a class="sourceLine" id="cb1-14" title="14">X fold Y</a>
<a class="sourceLine" id="cb1-15" title="15">X map Y</a>
<a class="sourceLine" id="cb1-16" title="16"></a>
<a class="sourceLine" id="cb1-17" title="17">$ mapa <span class="st">'$1*2'</span> <span class="dt">{1..3}</span></a>
<a class="sourceLine" id="cb1-18" title="18">2</a>
<a class="sourceLine" id="cb1-19" title="19">4</a>
<a class="sourceLine" id="cb1-20" title="20">6</a>
<a class="sourceLine" id="cb1-21" title="21"></a>
<a class="sourceLine" id="cb1-22" title="22">$ mapl <span class="st">'echo result $1'</span> <span class="ot">$(</span>mapa <span class="st">'$1+5'</span> <span class="ot">$(</span>mapa <span class="st">'$1*2'</span> <span class="dt">{1..3}</span><span class="ot">))</span></a>
<a class="sourceLine" id="cb1-23" title="23">result 7</a>
<a class="sourceLine" id="cb1-24" title="24">result 9</a>
<a class="sourceLine" id="cb1-25" title="25">result 11</a></code></pre></div>
<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> some simple implementation of higher order function for zsh.</p>
</div>
<p>Why is it important to have these functions? Simply because, the more I programmed with zsh the more I tended to work using functional programming style.</p>
<p>The minimal to have better code are the functions <code>map</code>, <code>filter</code> and <code>fold</code>.</p>
<p>Lets compare. First a program which convert all gif to png in many different directories of different projects.</p>
<p>Before ⇒</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode zsh"><code class="sourceCode zsh"><a class="sourceLine" id="cb2-1" title="1"><span class="co"># for each directory in projects dir</span></a>
<a class="sourceLine" id="cb2-2" title="2"><span class="kw">for</span> toProject <span class="kw">in</span> /path/to/projects/*<span class="kw">(</span>/N<span class="kw">)</span>; <span class="kw">do</span></a>
<a class="sourceLine" id="cb2-3" title="3"> <span class="co"># toProject is /path/to/projects/foo</span></a>
<a class="sourceLine" id="cb2-4" title="4"> <span class="co"># project become foo (:t for tail)</span></a>
<a class="sourceLine" id="cb2-5" title="5"> <span class="ot">project=${toProject:t}</span></a>
<a class="sourceLine" id="cb2-6" title="6"> <span class="kw">for</span> toResource <span class="kw">in</span> <span class="ot">$toProject</span>/resources/*.gif<span class="kw">(</span>.N<span class="kw">)</span>; <span class="kw">do</span></a>
<a class="sourceLine" id="cb2-7" title="7"> convert <span class="ot">$toResource</span> <span class="ot">${toResource:r}</span>.png <span class="kw">&amp;&amp;</span> <span class="kw">\</span></a>
<a class="sourceLine" id="cb2-8" title="8"> \<span class="kw">rm</span> -f <span class="ot">$toResource</span></a>
<a class="sourceLine" id="cb2-9" title="9"> <span class="kw">done</span></a>
<a class="sourceLine" id="cb2-10" title="10"><span class="kw">done</span></a></code></pre></div>
<ul>
<li>The <code>(/N)</code> means to select only directory and not to crash if there isnt any.</li>
<li>The <code>(.N)</code> means to select only files and not to crash if there isnt any.</li>
<li>The <code>:t</code> means tail; if <code>toto=/path/to/file.ext</code> then <code>${toto:t}=file.ext</code>.</li>
</ul>
<p>After ⇒</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb3-1" title="1"><span class="fu">gif_to_png()</span> <span class="kw">{</span> <span class="ex">convert</span> <span class="va">$1</span> <span class="va">${1:r}</span>.png <span class="kw">&amp;&amp;</span> \<span class="fu">rm</span> -f <span class="va">$1</span> <span class="kw">}</span></a>
<a class="sourceLine" id="cb3-2" title="2"></a>
<a class="sourceLine" id="cb3-3" title="3"><span class="fu">handle_resources()</span> <span class="kw">{</span> <span class="ex">map</span> gif_to_png <span class="va">$1</span>/resources/*.gif(.N) <span class="kw">}</span></a>
<a class="sourceLine" id="cb3-4" title="4"></a>
<a class="sourceLine" id="cb3-5" title="5"><span class="ex">map</span> handle_resources /path/to/projects/*(/N)</a></code></pre></div>
<p>No more bloc! It might be a little bit harder to read if youre not used to functional programming notation. But it is more concise and robusts.</p>
<p>Another example with some tests.</p>
<p>Find all files in project not containing an <code>s</code> which their name contains their project name:</p>
<p>Before ⇒</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode zsh"><code class="sourceCode zsh"><a class="sourceLine" id="cb4-1" title="1"><span class="kw">for</span> toProject <span class="kw">in</span> Projects/*; <span class="kw">do</span></a>
<a class="sourceLine" id="cb4-2" title="2"> <span class="ot">project=$toProject</span>:t</a>
<a class="sourceLine" id="cb4-3" title="3"> <span class="kw">if</span> <span class="kw">print</span> -- project <span class="kw">|</span> <span class="kw">grep</span> -v s <span class="kw">&gt;</span>/dev/null</a>
<a class="sourceLine" id="cb4-4" title="4"> <span class="kw">then</span></a>
<a class="sourceLine" id="cb4-5" title="5"> <span class="kw">print</span> <span class="ot">$project</span></a>
<a class="sourceLine" id="cb4-6" title="6"> <span class="kw">for</span> toResource <span class="kw">in</span> <span class="ot">$toProject</span>/*<span class="kw">(</span>.N<span class="kw">)</span>; <span class="kw">do</span></a>
<a class="sourceLine" id="cb4-7" title="7"> <span class="kw">if</span> <span class="kw">print</span> -- <span class="ot">${toResource:t}</span> <span class="kw">|</span> <span class="kw">grep</span> <span class="ot">$project</span> <span class="kw">&gt;</span>/dev/null; <span class="kw">then</span></a>
<a class="sourceLine" id="cb4-8" title="8"> <span class="kw">print</span> -- <span class="st">&quot;X </span><span class="ot">$toResource</span><span class="st">&quot;</span></a>
<a class="sourceLine" id="cb4-9" title="9"> <span class="kw">fi</span></a>
<a class="sourceLine" id="cb4-10" title="10"> <span class="kw">done</span></a>
<a class="sourceLine" id="cb4-11" title="11"> <span class="kw">fi</span></a>
<a class="sourceLine" id="cb4-12" title="12"><span class="kw">done</span></a></code></pre></div>
<p>After ⇒</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode zsh"><code class="sourceCode zsh"><a class="sourceLine" id="cb5-1" title="1"><span class="fu">contain_no_s()</span> <span class="kw">{</span> <span class="kw">print</span> <span class="ot">$1</span> <span class="kw">|</span> <span class="kw">grep</span> -v s <span class="kw">}</span></a>
<a class="sourceLine" id="cb5-2" title="2"></a>
<a class="sourceLine" id="cb5-3" title="3"><span class="kw">function</span><span class="fu"> verify_file_name</span> <span class="kw">{</span> </a>
<a class="sourceLine" id="cb5-4" title="4"> <span class="kw">local</span> <span class="ot">project=$1</span>:t</a>
<a class="sourceLine" id="cb5-5" title="5"> <span class="fu">contains_project_name()</span> <span class="kw">{</span> <span class="kw">print</span> <span class="ot">$1</span>:t <span class="kw">|</span> <span class="kw">grep</span> <span class="ot">$project</span> <span class="kw">}</span></a>
<a class="sourceLine" id="cb5-6" title="6"> map <span class="st">&quot;print -- X&quot;</span> <span class="ot">$(</span>filter contains_project_name <span class="ot">$1</span>/*<span class="kw">(</span>.N<span class="kw">)</span><span class="ot">)</span></a>
<a class="sourceLine" id="cb5-7" title="7"><span class="kw">}</span></a>
<a class="sourceLine" id="cb5-8" title="8"></a>
<a class="sourceLine" id="cb5-9" title="9">map verify_file_name <span class="ot">$(</span> filter contain_no_s Projects/* <span class="ot">)</span></a></code></pre></div>
<p>Also, the first verstion is a bit easier to read. But the second one is clearly far superior in architecture. I dont want to argue why here. Just believe me that the functional programming approach is superior.</p>
<p>You can find an <a href="https://github.com/Tarrasch/zsh-functional">updated version of the code (thanks to Arash Rouhani)</a>. An older version is <a href="https://github.com/yogsototh/zsh_functional">here thought</a>. Here is the (first version) source code:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode zsh"><code class="sourceCode zsh"><a class="sourceLine" id="cb6-1" title="1"><span class="co">#!/usr/bin/env zsh</span></a>
<a class="sourceLine" id="cb6-2" title="2"></a>
<a class="sourceLine" id="cb6-3" title="3"><span class="co"># Provide higer-order functions </span></a>
<a class="sourceLine" id="cb6-4" title="4"></a>
<a class="sourceLine" id="cb6-5" title="5"><span class="co"># usage:</span></a>
<a class="sourceLine" id="cb6-6" title="6"><span class="co">#</span></a>
<a class="sourceLine" id="cb6-7" title="7"><span class="co"># $ foo(){print &quot;x: $1&quot;}</span></a>
<a class="sourceLine" id="cb6-8" title="8"><span class="co"># $ map foo a b c d</span></a>
<a class="sourceLine" id="cb6-9" title="9"><span class="co"># x: a</span></a>
<a class="sourceLine" id="cb6-10" title="10"><span class="co"># x: b</span></a>
<a class="sourceLine" id="cb6-11" title="11"><span class="co"># x: c</span></a>
<a class="sourceLine" id="cb6-12" title="12"><span class="co"># x: d</span></a>
<a class="sourceLine" id="cb6-13" title="13"><span class="kw">function</span><span class="fu"> map</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-14" title="14"> <span class="kw">local</span> <span class="ot">func_name=$1</span></a>
<a class="sourceLine" id="cb6-15" title="15"> <span class="kw">shift</span></a>
<a class="sourceLine" id="cb6-16" title="16"> <span class="kw">for</span> elem <span class="kw">in</span> <span class="ot">$@</span>; <span class="kw">print</span> -- <span class="ot">$(</span><span class="kw">eval</span> <span class="ot">$func_name</span> <span class="ot">$elem)</span></a>
<a class="sourceLine" id="cb6-17" title="17"><span class="kw">}</span></a>
<a class="sourceLine" id="cb6-18" title="18"></a>
<a class="sourceLine" id="cb6-19" title="19"><span class="co"># $ bar() { print $(($1 + $2)) }</span></a>
<a class="sourceLine" id="cb6-20" title="20"><span class="co"># $ fold bar 0 1 2 3 4 5</span></a>
<a class="sourceLine" id="cb6-21" title="21"><span class="co"># 15</span></a>
<a class="sourceLine" id="cb6-22" title="22"><span class="co"># -- but also</span></a>
<a class="sourceLine" id="cb6-23" title="23"><span class="co"># $ fold bar 0 $( seq 1 100 )</span></a>
<a class="sourceLine" id="cb6-24" title="24"><span class="kw">function</span><span class="fu"> fold</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-25" title="25"> <span class="kw">if</span> <span class="kw">((</span><span class="ot">$#</span>&lt;2<span class="kw">))</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-26" title="26"> <span class="kw">print</span> -- <span class="st">&quot;ERROR fold use at least 2 arguments&quot;</span> <span class="kw">&gt;&amp;2</span></a>
<a class="sourceLine" id="cb6-27" title="27"> <span class="kw">return</span> 1</a>
<a class="sourceLine" id="cb6-28" title="28"> <span class="kw">}</span></a>
<a class="sourceLine" id="cb6-29" title="29"> <span class="kw">if</span> <span class="kw">((</span><span class="ot">$#</span>&lt;3<span class="kw">))</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-30" title="30"> <span class="kw">print</span> -- <span class="ot">$2</span></a>
<a class="sourceLine" id="cb6-31" title="31"> <span class="kw">return</span> 0</a>
<a class="sourceLine" id="cb6-32" title="32"> <span class="kw">}</span> <span class="kw">else</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-33" title="33"> <span class="kw">local</span> <span class="ot">acc</span></a>
<a class="sourceLine" id="cb6-34" title="34"> <span class="kw">local</span> <span class="ot">right</span></a>
<a class="sourceLine" id="cb6-35" title="35"> <span class="kw">local</span> <span class="ot">func_name=$1</span></a>
<a class="sourceLine" id="cb6-36" title="36"> <span class="kw">local</span> <span class="ot">init_value=$2</span></a>
<a class="sourceLine" id="cb6-37" title="37"> <span class="kw">local</span> <span class="ot">first_value=$3</span></a>
<a class="sourceLine" id="cb6-38" title="38"> <span class="kw">shift</span> 3</a>
<a class="sourceLine" id="cb6-39" title="39"> <span class="ot">right=$(</span> fold <span class="ot">$func_name</span> <span class="ot">$init_value</span> <span class="ot">$@</span> <span class="ot">)</span></a>
<a class="sourceLine" id="cb6-40" title="40"> <span class="ot">acc=$(</span> <span class="kw">eval</span> <span class="st">&quot;</span><span class="ot">$func_name</span><span class="st"> </span><span class="ot">$first_value</span><span class="st"> </span><span class="ot">$right</span><span class="st">&quot;</span> <span class="ot">)</span></a>
<a class="sourceLine" id="cb6-41" title="41"> <span class="kw">print</span> -- <span class="ot">$acc</span></a>
<a class="sourceLine" id="cb6-42" title="42"> <span class="kw">return</span> 0</a>
<a class="sourceLine" id="cb6-43" title="43"> <span class="kw">}</span></a>
<a class="sourceLine" id="cb6-44" title="44"><span class="kw">}</span></a>
<a class="sourceLine" id="cb6-45" title="45"></a>
<a class="sourceLine" id="cb6-46" title="46"><span class="co"># usage:</span></a>
<a class="sourceLine" id="cb6-47" title="47"><span class="co">#</span></a>
<a class="sourceLine" id="cb6-48" title="48"><span class="co"># $ baz() { print $1 | grep baz }</span></a>
<a class="sourceLine" id="cb6-49" title="49"><span class="co"># $ filter baz titi bazaar biz</span></a>
<a class="sourceLine" id="cb6-50" title="50"><span class="co"># bazaar</span></a>
<a class="sourceLine" id="cb6-51" title="51"><span class="kw">function</span><span class="fu"> filter</span> <span class="kw">{</span></a>
<a class="sourceLine" id="cb6-52" title="52"> <span class="kw">local</span> <span class="ot">predicate=$1</span></a>
<a class="sourceLine" id="cb6-53" title="53"> <span class="kw">local</span> <span class="ot">result</span></a>
<a class="sourceLine" id="cb6-54" title="54"> <span class="kw">typeset</span> -a <span class="ot">result</span></a>
<a class="sourceLine" id="cb6-55" title="55"> <span class="kw">shift</span></a>
<a class="sourceLine" id="cb6-56" title="56"> <span class="kw">for</span> elem <span class="kw">in</span> <span class="ot">$@</span>; <span class="kw">do</span></a>
<a class="sourceLine" id="cb6-57" title="57"> <span class="kw">if</span> <span class="kw">eval</span> <span class="ot">$predicate</span> <span class="ot">$elem</span> <span class="kw">&gt;</span>/dev/null; <span class="kw">then</span></a>
<a class="sourceLine" id="cb6-58" title="58"> <span class="ot">result=(</span> <span class="ot">$result</span> <span class="ot">$elem</span> <span class="ot">)</span></a>
<a class="sourceLine" id="cb6-59" title="59"> <span class="kw">fi</span></a>
<a class="sourceLine" id="cb6-60" title="60"> <span class="kw">done</span></a>
<a class="sourceLine" id="cb6-61" title="61"> <span class="kw">print</span> <span class="ot">$result</span></a>
<a class="sourceLine" id="cb6-62" title="62"><span class="kw">}</span></a></code></pre></div>
</div>
<div id="afterarticle">
<div id="social">
2021-05-25 20:25:47 +00:00
<a href="/rss.xml" target="_blank" rel="noopener noreferrer nofollow" class="social">RSS</a>
2021-04-18 10:23:24 +00:00
·
<a href="https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Higher-order-function-in-zsh/%20via%20@yogsototh" target="_blank" rel="noopener noreferrer nofollow" class="social">Tweet</a>
·
<a href="http://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fyannesposito.com/Scratch/en/blog/Higher-order-function-in-zsh/" target="_blank" rel="noopener noreferrer nofollow" class="social">FB</a>
<br />
<a class="message" href="../../../../Scratch/en/blog/Social-link-the-right-way/">These social sharing links preserve your privacy</a>
</div>
<div id="navigation">
<a href="../../../../">Home</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/blog">Blog</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/softwares">Softwares</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/en/about">About</a>
</div>
<div id="totop"><a href="#header">↑ Top ↑</a></div>
<div id="bottom">
<div>
Published on 2011-09-28
</div>
<div>
<a href="https://twitter.com/yogsototh">Follow @yogsototh</a>
</div>
<div>
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US">Yann Esposito©</a>
</div>
<div>
Done with
<a href="http://www.vim.org" target="_blank" rel="noopener noreferrer nofollow"><strike>Vim</strike></a>
<a href="http://spacemacs.org" target="_blank" rel="noopener noreferrer nofollow">spacemacs</a>
<span class="pala">&amp;</span>
<a href="http://nanoc.ws" target="_blank" rel="noopener noreferrer nofollow"><strike>nanoc</strike></a>
<a href="http://jaspervdj.be/hakyll" target="_blank" rel="noopener noreferrer nofollow">Hakyll</a>
</div>
<hr />
<div style="max-width: 100%">
<a href="https://cardanohub.org">
<img src="../../../../Scratch/img/ada-logo.png" class="simple" style="height: 16px;
border-radius: 50%;
vertical-align:middle;
display:inline-block;" />
ADA:
</a>
<code style="display:inline-block;
word-wrap:break-word;
text-align: left;
vertical-align: top;
max-width: 85%;">
DdzFFzCqrhtAvdkmATx5Fm8NPJViDy85ZBw13p4XcNzVzvQg8e3vWLXq23JQWFxPEXK6Kvhaxxe7oJt4VMYHxpA2vtCFiP8fziohN6Yp
</code>
</div>
</div>
</div>
</div>
</div>
</body>
</html>