在 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)
然后它的工作原理。根据我迄今为止所学到的关于($)的内容,不应该两者相等吗? ($)是正确的联想,因此在我看来应该发生的事情是
dropWhile (/=' ') str
drop 1 (dropWhile (/=' ') str)
splitString
。相反,我收到来自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
应该是指。
答案 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...