Haskell $运算符:为什么这不起作用?

时间:2016-09-11 03:45:41

标签: haskell

Haskell Programming from First Principles 一书中,有一个练习告诉我们编写一个函数,它接受一个带有空格的字符串,用空格分割它,并加载非空间块到一个字符串列表。我的第一次尝试是:

splitString :: String -> [String]
splitString str
  | str == "" = []
  | otherwise = takeWhile (/=' ') str : splitString $ drop 1 $ dropWhile (/=' ') str

现在这不会编译。如果我用相应的括号括号替换第一个($)(在splitString之后),而不是这样:

takeWhile (/=' ') str : splitString (drop 1 $ dropWhile (/=' ') str)

然后它的工作原理。根据我迄今为止所学到的关于($)的内容,不应该两者相等吗? ($)是正确的联想,因此在我看来应该发生的事情是

    首先评估
  1. dropWhile (/=' ') str
  2. 接下来是
  3. drop 1 (dropWhile (/=' ') str)
  4. 然后将结果传递到splitString
  5. 相反,我收到来自ghc的错误

    Couldn't match expected type ‘[Char] -> [String]’
                with actual type ‘[[Char]]’
    The first argument of ($) takes one argument,
    but its type ‘[[Char]]’ has none
    

    我可以通过"第一个参数($)"它在谈论splitString,但我对这句话的含义感到困惑

    but its type `[[Char]]` has none
    

    应该是指。

1 个答案:

答案 0 :(得分:8)

如果您添加如下所示的parens,您的代码将起作用:

...
  | otherwise = takeWhile (/=' ') str : ( splitString $ drop 1 $ dropWhile (/=' ') str )
--                                     ^^^                                            ^^^                                                                                        

否则Haskell将else子句解释为:

( takeWhile (/=' ') str : splitString )
    $ drop 1
    $ dropWhile (/= ' ') str

<强>更新

您在评论中提到的版本:

     takeWhile (/= ' ') str : splitString ( ... )
--   \__ a __/ \_ b _/   c  : \___ d ___/ \_ e _/

的格式为a b c : d e,Haskell始终将其解释为(a b c) : (d e),因为:是唯一出现该表达式的中缀运算符。

当你有类似的东西时:

    a b c : d e $ f $ g

您必须考虑:$中缀运算符的相对优先级。由于$defined as infixr 0,因此不是: 绑定与 (a b c : d e) $ (f $ g) 紧密相关,您将获得以下右关联分组:

namespace ElementFlowExample
{



#region Using Statements:



using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;

using FluidKit.Controls;
using FluidKit.Showcase.ElementFlow;



#endregion




/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{



    #region Fields:


    private StringCollection _dataSource;

    private LayoutBase[] _layouts = {
                                        new Wall(),
                                        new SlideDeck(),
                                        new CoverFlow(),
                                        new Carousel(),
                                        new TimeMachine2(),
                                        new ThreeLane(),
                                        new VForm(),
                                        new TimeMachine(),
                                        new RollerCoaster(),
                                        new Rolodex(),
                                    };

    private Random _randomizer = new Random();

    private int _viewIndex;


    #endregion




    #region Properties:


    #endregion




    public MainWindow()
    {
        InitializeComponent();
    }




    private void Window_Loaded(object sender, RoutedEventArgs e)
    {

        _elementFlow.Layout = _layouts[3];
        _currentViewText.Text = _elementFlow.Layout.GetType().Name;

        _selectedIndexSlider.Maximum = _elementFlow.Items.Count - 1;
        _elementFlow.SelectionChanged += EFSelectedIndexChanged;
        _elementFlow.SelectedIndex = 0;

        _dataSource = FindResource("DataSource") as StringCollection;


    }




    private void EFSelectedIndexChanged(object sender, SelectionChangedEventArgs e)
    {
        Debug.WriteLine((sender as ElementFlow).SelectedIndex);
    }




    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.F12)
        {
            _viewIndex = (_viewIndex + 1) % _layouts.Length;
            _elementFlow.Layout = _layouts[_viewIndex];
            _currentViewText.Text = _elementFlow.Layout.GetType().Name;
        }
    }




    private void ChangeSelectedIndex(object sender, RoutedPropertyChangedEventArgs<double> args)
    {
        _elementFlow.SelectedIndex = (int)args.NewValue;
    }




    private void RemoveCard(object sender, RoutedEventArgs args)
    {
        if (_elementFlow.Items.Count > 0)
        {
            _dataSource.RemoveAt(_randomizer.Next(_dataSource.Count));

            // Update selectedindex slider
            _selectedIndexSlider.Maximum = _elementFlow.Items.Count - 1;
        }
    }




    private void AddCard(object sender, RoutedEventArgs args)
    {
        Button b = sender as Button;
        int index = _randomizer.Next(_dataSource.Count);
        if (b.Name == "_regular")
        {
            _dataSource.Insert(index, "Images/01.jpg");
        }
        else
        {
            _dataSource.Insert(index, string.Format("Images/{0:00}", _randomizer.Next(1, 12)) + ".jpg");
        }

        // Update selectedindex slider
        _selectedIndexSlider.Maximum = _elementFlow.Items.Count - 1;
    }




} // END of Class...

} // END of Namespace...