flatMap究竟做了什么?

时间:2015-11-28 12:32:30

标签: scala flatmap

对于学校的编码工作,我必须做平板图的工作,但我完全不知道它做了什么,我已经在网上阅读了几页并阅读了我的教科书,但我仍然没有真正理解它的作用。我知道地图有什么作用,但由于某种原因,我很难将头部包裹在flatmap中。有人可以帮忙吗?谢谢。

只是添加更多信息 - 当我在线查看示例时,我会看到flatmap如何返回与地图不同的内容。但是当flatmap被调用时,它实际上在做什么呢? flatmap如何实际工作?它在返回结果之前做了什么?

3 个答案:

答案 0 :(得分:6)

这是一个类比。

想象一下,你有一个装满纸箱的购物券的大包。如果你有一个“使用优惠券购买一盒鸡蛋”的功能,而你打电话给bigBagOfVouchers.map(buyCartonOfEggs),你就会得到一袋纸箱。

但是,如果你打电话给bigBagOfVouchers.flatMap(buyCartonOfEggs),你就会有一袋鸡蛋 - 没有任何纸箱。

flatMap将结果展平一级。可能Bag[Carton[Egg]]现在是Bag[Egg]

答案 1 :(得分:5)

Functors定义具有类型

的地图
trait Functor[F[_]] {
    def map[A, B](f: A => B)(v: F[A]): F[B]
}

Monads是函数,它支持两个额外的操作:

trait Monad[M[_]] extends Functor[M] {
    def pure[A](v: A): M[A]
    def join[A](m: M[M[A]]): M[A]
}

加入展平嵌套值,例如如果mList,则join的类型为

def joinList[A](l: List[List[A]]): List[A]

如果你有一个monad m而你map,那么如果b是同一个monadic类型会怎样?例如:

def replicate[A](i: Int, value: A): List[A] = ???
val f = new Functor[List] {
    def map[A, B](f: A => B)(v: List[A]) = v.map(f)
}

然后

f.map(x => replicate(x, x))(List(1,2,3)) == List(List(1), List(2,2), List(3,3,3))

此类型为List[List[Int]],而输入为List[Int]。对于希望每个步骤返回相同输入类型的操作链而言,这是相当常见的。由于List也可以制作为monad,因此您可以使用join轻松创建此类列表:

listMonad.join(List(List(1), List(2,2), List(3,3,3))) == List(1,2,2,3,3,3)

现在您可能想编写一个函数将这两个操作合并为一个:

trait Monad[M] {
   def flatMap[A, B](f: A => M[B])(m: M[A]): M[B] = join(map(f)(m))
}

然后你就可以做到:

listMonad.flatMap(List(1,2,3), x => replicate(x, x)) == List(1,2,2,3,3,3)

flatMap完全取决于monad类型构造函数M(本例中为List),因为它取决于mapjoin

答案 2 :(得分:0)

在反应式编程中,您经常遇到需要使用 flatMap Future [Future [List]] 转换为 Future [List]的情况] 。例如,您有两个功能: get 来自数据库的用户和进程检索到的用户;并返回 Future [List [User]] 。如果您将地图应用于获取进程,结果将是 Future [Future [List [User]]] 这没有任何意义。相反,您应该使用flatMap:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Diagnostics;
using System.Net;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string Address= "*PUT IP ADDRESS HERE WHERE UDP SERVER IS*";
            int UDPPort = *PUT UDP SERVER PORT HERE*;
            UdpRedirect _UdpRedirect = new UdpRedirect() { _address = Address, _Port = UDPPort};
            Thread _Thread = new Thread(_UdpRedirect.Connect);
            _Thread.Name = "UDP";
            _Thread.Start();

            int TCPPort = *PUT TCP PORT HERE FOR TCP PROXY*;       
            TcpRedirect _TcpRedirect = new TcpRedirect(Address, TCPPort);            
        }
    }
    class UdpRedirect
    {
        public string _address;
        public int _Port;
        public UdpRedirect()
        {
        }

        public void Connect()
        {
            UdpClient _UdpClient = new UdpClient(_Port);
            int? LocalPort = null;
            while (true)
            {
                IPEndPoint _IPEndPoint = null;
                byte[] _bytes = _UdpClient.Receive(ref _IPEndPoint);
                if (LocalPort == null) LocalPort = _IPEndPoint.Port;
                bool Local = IPAddress.IsLoopback(_IPEndPoint.Address);
                string AddressToSend = null;
                int PortToSend = 0;
                if (Local)
                {
                    AddressToSend = _address;
                    PortToSend = _Port;
                }
                else
                {
                    AddressToSend = "127.0.0.1";
                    PortToSend = LocalPort.Value;
                }
                _UdpClient.Send(_bytes, _bytes.Length, AddressToSend, PortToSend);
            }
        }
    }
    class TcpRedirect
    {
        public TcpRedirect(string _address, int _Port)
        {

            TcpListener _TcpListener = new TcpListener(IPAddress.Any, _Port);
            _TcpListener.Start();
            int i = 0;
            while (true)
            {
                i++;
                TcpClient _LocalSocket = _TcpListener.AcceptTcpClient();
                NetworkStream _NetworkStreamLocal = _LocalSocket.GetStream();

                TcpClient _RemoteSocket = new TcpClient(_address, _Port);
                NetworkStream _NetworkStreamRemote = _RemoteSocket.GetStream();
                Console.WriteLine("\n<<<<<<<<<connected>>>>>>>>>>>>>");
                Client _RemoteClient = new Client("remote" + i)
                {
                    _SendingNetworkStream = _NetworkStreamLocal,
                    _ListenNetworkStream = _NetworkStreamRemote,
                    _ListenSocket = _RemoteSocket
                };
                Client _LocalClient = new Client("local" + i)
                {
                    _SendingNetworkStream = _NetworkStreamRemote,
                    _ListenNetworkStream = _NetworkStreamLocal,
                    _ListenSocket = _LocalSocket
                };
            }
        }
        public class Client
        {
            public TcpClient _ListenSocket;
            public NetworkStream _SendingNetworkStream;
            public NetworkStream _ListenNetworkStream;
            Thread _Thread;
            public Client(string Name)
            {
                _Thread = new Thread(new ThreadStart(ThreadStartHander));
                _Thread.Name = Name;
                _Thread.Start();
            }
            public void ThreadStartHander()
            {
                Byte[] data = new byte[99999];
                while (true)
                {
                    if (_ListenSocket.Available > 0)
                    {
                        int _bytesReaded = _ListenNetworkStream.Read(data, 0, _ListenSocket.Available);
                        _SendingNetworkStream.Write(data, 0, _bytesReaded);
                        Console.WriteLine("(((((((" + _bytesReaded + "))))))))))" + _Thread.Name + "\n" + ASCIIEncoding.ASCII.GetString(data, 0, _bytesReaded).Replace((char)7, '?'));
                    }
                    Thread.Sleep(10);
                }
            }

        }
    }
}