her.esy.fun/src/Scratch/fr/blog/Higher-order-function-in-zsh/index.html
Yann Esposito (Yogsototh) 059fabd7d0
many minor details to update
2022-10-26 11:38:50 +02:00

239 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>YBlog - Fonctions d'ordre supérieur en 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" />
<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" />
<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="fr" class="article">
<div id="content">
<div id="header">
<div id="choix">
<span id="choixlang">
<a href="../../../../Scratch/en/blog/Higher-order-function-in-zsh/">Anglais</a>
</span>
<span class="tomenu"><a href="#navigation">↓ Menu ↓</a></span>
<span class="flush"></span>
</div>
</div>
<div id="titre">
<h1>Fonctions d'ordre supérieur en 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 a découvert un moyen de faire des fonctions anonymes</a> Merci!</p>
<p>Avec cette dernière version vous pouvez utiliser <code>map</code> si vous utilisez des fonctions déclarées. <code>mapl</code> pour les fonctions anonymes et <code>mapa</code> pour les fonctions arithmétiques.</p>
<p>Exemple&nbsp;:</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="Trop long; pas lu">tlpl</abbr>: </span> des fonctions dordres supérieurs en zsh.</p>
</div>
<p>Tout dabord, pourquoi cest important davoir ces fonctions. Plus je programmais avec zsh plus jessayais davoir un style fonctionnel.</p>
<p>Le minimum pour pouvoir avoir du code plus lisible cest de posséder les fonctions <code>map</code>, <code>filter</code> et <code>fold</code>.</p>
<p>Voici pourquoi avec une comparaison. Commençons par un programme qui converti tous les gif en png dans plusieurs répertoires projets contenant tous des répertoires resources. Avant&nbsp;:</p>
<p>Avant ⇒</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>Le <code>(/N)</code> permet de sélectionner seulement les répertoires sans casser la boucle sil ny a pas de “match”.</li>
<li>Le <code>(.N)</code> permet de sélection seulement les fichiers, aussi sans tout arréter sil ne trouve rien.</li>
<li>Le <code>:t</code> signfie “tail” ; si <code>toto=/path/to/file.ext</code> alors <code>${toto:t}=file.ext</code>.</li>
</ul>
<p>Après</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>Plus de bloc ! Oui, cest un poil plus difficile à lire pour les non initiés. Mais cest à la fois plus concis et plus robuste.</p>
<p>Et encore ce code ne possède pas de test. Recommençons sur le même principe.</p>
<p>Trouver les fichiers des projets qui ne contiennent pas de s dans leur nom qui ont le même nom que leur projet.</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>La première version peu paraître plus facile à lire. Mais la seconde est plus bien supérieure en terme darchitecture. Je ne veux pas discuster ici pourquoi cest mieux. Je vous demande simplement de me croire quand je dis que lapproche fonctionnelle est supérieure.</p>
<p>Vous pouvez télécharger <a href="https://github.com/Tarrasch/zsh-functional">une version à jour du code (merci à Arash Rouhani)</a>. Une ancienne version est <a href="https://github.com/yogsototh/zsh_functional">ici</a>. Voici le code source (de la première version)&nbsp;:</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">
<a href="/rss.xml" target="_blank" rel="noopener noreferrer nofollow" class="social">RSS</a>
·
<a href="https://twitter.com/home?status=http%3A%2F%2Fyannesposito.com/Scratch/fr/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/fr/blog/Higher-order-function-in-zsh/" target="_blank" rel="noopener noreferrer nofollow" class="social">FB</a>
<br />
<a class="message" href="../../../../Scratch/fr/blog/Social-link-the-right-way/">Ces liens sociaux préservent votre vie privée</a>
</div>
<div id="navigation">
<a href="../../../../">Accueil</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/fr/blog">Blog</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/fr/softwares">Logiciels</a>
<span class="sep">¦</span>
<a href="../../../../Scratch/fr/about">Auteur</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>
</div>
</div>
</div>
</div>
</body>
</html>